camstack 1.2.203 → 1.2.205

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.
@@ -24024,9 +24024,9 @@ var require_zod = __commonJS({
24024
24024
  }
24025
24025
  });
24026
24026
 
24027
- // ../system/dist/dist-QyW9hVO2.js
24028
- var require_dist_QyW9hVO2 = __commonJS({
24029
- "../system/dist/dist-QyW9hVO2.js"(exports) {
24027
+ // ../system/dist/dist-DuBtzSCh.js
24028
+ var require_dist_DuBtzSCh = __commonJS({
24029
+ "../system/dist/dist-DuBtzSCh.js"(exports) {
24030
24030
  "use strict";
24031
24031
  var zod = require_zod();
24032
24032
  __require("crypto");
@@ -27460,6 +27460,9 @@ var require_dist_QyW9hVO2 = __commonJS({
27460
27460
  field: zod.z.string().min(1),
27461
27461
  on: zod.z.literal("advance")
27462
27462
  });
27463
+ function historyItemFieldPath(itemsPath, field) {
27464
+ return `${itemsPath}[].${field}`;
27465
+ }
27463
27466
  var HISTORY_DEFAULT_MIN_INTERVAL_MS = 1e4;
27464
27467
  function resolveCapMount(def) {
27465
27468
  if (def.mount) return def.mount;
@@ -27481,7 +27484,11 @@ var require_dist_QyW9hVO2 = __commonJS({
27481
27484
  zod.z.literal(4)
27482
27485
  ]).optional(),
27483
27486
  tab: zod.z.string().optional(),
27484
- location: zod.z.enum(["settings", "top-tab"]).optional(),
27487
+ location: zod.z.enum([
27488
+ "settings",
27489
+ "top-tab",
27490
+ "hero"
27491
+ ]).optional(),
27485
27492
  order: zod.z.number().optional(),
27486
27493
  fields: zod.z.array(zod.z.any())
27487
27494
  });
@@ -32079,7 +32086,11 @@ var require_dist_QyW9hVO2 = __commonJS({
32079
32086
  zod.z.literal(4)
32080
32087
  ]).optional(),
32081
32088
  tab: zod.z.string().optional(),
32082
- location: zod.z.enum(["settings", "top-tab"]).optional(),
32089
+ location: zod.z.enum([
32090
+ "settings",
32091
+ "top-tab",
32092
+ "hero"
32093
+ ]).optional(),
32083
32094
  order: zod.z.number().optional(),
32084
32095
  fields: zod.z.array(zod.z.any())
32085
32096
  });
@@ -37376,6 +37387,46 @@ var require_dist_QyW9hVO2 = __commonJS({
37376
37387
  approxBytes: zod.z.number().nullable(),
37377
37388
  countedAt: zod.z.number().nullable()
37378
37389
  });
37390
+ var DeviceHistoryOrphanReasonSchema = zod.z.enum(["device-gone", "undeclared"]);
37391
+ var TierRowsSchema = zod.z.object({
37392
+ raw: zod.z.number().int(),
37393
+ "5m": zod.z.number().int(),
37394
+ "1h": zod.z.number().int(),
37395
+ "1d": zod.z.number().int()
37396
+ });
37397
+ var OrphanSeriesSchema = zod.z.object({
37398
+ seriesId: zod.z.number().int(),
37399
+ deviceId: zod.z.number().int(),
37400
+ key: zod.z.string(),
37401
+ scope: zod.z.string(),
37402
+ /** The metric's display name; `null` when its metric row is gone too. */
37403
+ displayName: zod.z.string().nullable(),
37404
+ reason: DeviceHistoryOrphanReasonSchema,
37405
+ /** `device-gone`: since when the device has been absent; `null` otherwise. */
37406
+ missingSince: zod.z.number().nullable(),
37407
+ firstTs: zod.z.number(),
37408
+ lastTs: zod.z.number(),
37409
+ rows: TierRowsSchema
37410
+ });
37411
+ var DeviceHistoryOrphanDeleteRefusalSchema = zod.z.enum([
37412
+ "not-found",
37413
+ "not-orphan",
37414
+ "failed"
37415
+ ]);
37416
+ var OrphanDeleteEntrySchema = zod.z.discriminatedUnion("ok", [zod.z.object({
37417
+ seriesId: zod.z.number().int(),
37418
+ ok: zod.z.literal(true),
37419
+ deleted: TierRowsSchema
37420
+ }), zod.z.object({
37421
+ seriesId: zod.z.number().int(),
37422
+ ok: zod.z.literal(false),
37423
+ reason: DeviceHistoryOrphanDeleteRefusalSchema
37424
+ })]);
37425
+ var OrphanListSchema = zod.z.object({
37426
+ series: zod.z.array(OrphanSeriesSchema).readonly(),
37427
+ /** More orphans exist than {@link DEVICE_HISTORY_ORPHAN_LIST_MAX}. */
37428
+ truncated: zod.z.boolean()
37429
+ });
37379
37430
  var DeviceHistoryStatusSchema = zod.z.object({
37380
37431
  startedAt: zod.z.number(),
37381
37432
  /** The store is declared, series loaded and the first reconcile has answered. */
@@ -37517,7 +37568,24 @@ var require_dist_QyW9hVO2 = __commonJS({
37517
37568
  kind: "mutation",
37518
37569
  auth: "admin"
37519
37570
  }),
37520
- getStatus: method(zod.z.object({}), DeviceHistoryStatusSchema, { auth: "admin" })
37571
+ getStatus: method(zod.z.object({}), DeviceHistoryStatusSchema, { auth: "admin" }),
37572
+ /**
37573
+ * Device series no longer linked to a device (D917): the device is absent
37574
+ * from the listings, or the key is declared by no capability. REPORTED,
37575
+ * never purged by this judgement; each entry counts its rows per tier
37576
+ * (a primary-key range per series). Ordered by device, key, scope.
37577
+ */
37578
+ listOrphanSeries: method(zod.z.object({}), OrphanListSchema, { auth: "admin" }),
37579
+ /**
37580
+ * Delete orphan series by id. Each id is re-judged NOW: a series that is
37581
+ * not orphan (its device came back) is refused `not-orphan`, an unknown id
37582
+ * `not-found`. Deletes tier by tier in the retention slices, then the
37583
+ * series row; one entry per id, in order.
37584
+ */
37585
+ deleteOrphanSeries: method(zod.z.object({ seriesIds: zod.z.array(zod.z.number().int()).min(1).max(50) }), zod.z.object({ results: zod.z.array(OrphanDeleteEntrySchema).readonly() }), {
37586
+ kind: "mutation",
37587
+ auth: "admin"
37588
+ })
37521
37589
  }
37522
37590
  };
37523
37591
  var DeviceCaStatusSchema = zod.z.discriminatedUnion("state", [
@@ -68611,7 +68679,6 @@ var require_dist_QyW9hVO2 = __commonJS({
68611
68679
  modelId: zod.z.string()
68612
68680
  }),
68613
68681
  zod.z.object({ kind: zod.z.literal("cpu-device") }),
68614
- zod.z.object({ kind: zod.z.literal("coral-untouched") }),
68615
68682
  zod.z.object({ kind: zod.z.literal("device-disabled") }),
68616
68683
  zod.z.object({ kind: zod.z.literal("device-unavailable") }),
68617
68684
  zod.z.object({ kind: zod.z.literal("node-offline") }),
@@ -68704,11 +68771,6 @@ var require_dist_QyW9hVO2 = __commonJS({
68704
68771
  kind: zod.z.literal("detector-store-unreadable"),
68705
68772
  error: zod.z.string()
68706
68773
  }),
68707
- zod.z.object({
68708
- kind: zod.z.literal("coral-enabled"),
68709
- nodeId: zod.z.string(),
68710
- deviceKey: zod.z.string()
68711
- }),
68712
68774
  zod.z.object({
68713
68775
  kind: zod.z.literal("custom-pin-kept"),
68714
68776
  nodeId: zod.z.string(),
@@ -76070,6 +76132,30 @@ var require_dist_QyW9hVO2 = __commonJS({
76070
76132
  * whether persisting is worth a SQLite commit. */
76071
76133
  volatileStateFields: ["lastChangedAt"]
76072
76134
  };
76135
+ var ConsumableKindSchema = zod.z.enum([
76136
+ "wear",
76137
+ "supply",
76138
+ "waste"
76139
+ ]);
76140
+ var ConsumableFormSchema = zod.z.enum([
76141
+ "part",
76142
+ "tank",
76143
+ "product",
76144
+ "bin"
76145
+ ]);
76146
+ var ConsumableStatusSchema = zod.z.enum([
76147
+ "ok",
76148
+ "low",
76149
+ "empty",
76150
+ "full",
76151
+ "replace"
76152
+ ]);
76153
+ zod.z.enum([
76154
+ "ok",
76155
+ "low",
76156
+ "empty",
76157
+ "full"
76158
+ ]);
76073
76159
  var ConsumableRemainingUnitSchema = zod.z.enum([
76074
76160
  "days",
76075
76161
  "hours",
@@ -76084,10 +76170,14 @@ var require_dist_QyW9hVO2 = __commonJS({
76084
76170
  key: zod.z.string().min(1),
76085
76171
  /** Display name. */
76086
76172
  label: zod.z.string().min(1),
76087
- /** Remaining life % when known (0..100). */
76173
+ /** Remaining life % (`wear`) or fill % (`supply`, `waste`) when known (0..100). */
76088
76174
  level: zod.z.number().min(0).max(100).nullable(),
76089
- /** Discrete state when known (binary mode). */
76090
- status: zod.z.enum(["ok", "replace"]).nullable(),
76175
+ /** The provider's discrete verdict when known (binary mode). */
76176
+ status: ConsumableStatusSchema.nullable(),
76177
+ /** What the item is — decides whether low or full is bad. Absent = `wear`. */
76178
+ kind: ConsumableKindSchema.optional(),
76179
+ /** How the item is drawn (part, tank, product, bin). Absent = derived from `kind`. */
76180
+ form: ConsumableFormSchema.optional(),
76091
76181
  /** Ms epoch of the last replace, when known. */
76092
76182
  lastResetAt: zod.z.number().nullable(),
76093
76183
  /** Whether `reset()` is meaningful for this item. */
@@ -76110,7 +76200,7 @@ var require_dist_QyW9hVO2 = __commonJS({
76110
76200
  kind: "widget",
76111
76201
  widgetId: "host/consumables-panel",
76112
76202
  tab: "consumables",
76113
- topTab: true,
76203
+ placement: "hero",
76114
76204
  label: "Consumables",
76115
76205
  order: 5
76116
76206
  } },
@@ -76172,9 +76262,37 @@ var require_dist_QyW9hVO2 = __commonJS({
76172
76262
  */
76173
76263
  durability: "restored",
76174
76264
  /**
76175
- * History (D850): none — `items` is an array keyed by runtime ids; a history path never enters it.
76265
+ * History (D850, D917): one series per (item field, item `key`) — the item key
76266
+ * is the series scope, the item `label` names it. A vacuum tank's fill, a
76267
+ * brush's remaining life and a dishwasher's salt are each recorded the moment
76268
+ * their provider reports them; an item that appears later gets its series.
76176
76269
  */
76177
- history: "none",
76270
+ history: {
76271
+ fields: {},
76272
+ items: {
76273
+ path: "items",
76274
+ key: "key",
76275
+ label: "label",
76276
+ fields: {
76277
+ level: {
76278
+ kind: "gauge",
76279
+ unit: "%",
76280
+ deadband: 2,
76281
+ displayName: "Level"
76282
+ },
76283
+ status: {
76284
+ kind: "enum",
76285
+ displayName: "Status"
76286
+ },
76287
+ "remaining.value": {
76288
+ kind: "gauge",
76289
+ unitFrom: "remaining.unit",
76290
+ deadband: 1,
76291
+ displayName: "Remaining"
76292
+ }
76293
+ }
76294
+ }
76295
+ },
76178
76296
  /** Clocks, not content: a re-read that changed no item costs no commit. */
76179
76297
  volatileStateFields: ["lastChangedAt", "lastFetchedAt"]
76180
76298
  };
@@ -76535,11 +76653,6 @@ var require_dist_QyW9hVO2 = __commonJS({
76535
76653
  "offline",
76536
76654
  "description-missing"
76537
76655
  ]);
76538
- var DishwasherConsumableLevelSchema = zod.z.enum([
76539
- "ok",
76540
- "low",
76541
- "empty"
76542
- ]);
76543
76656
  var DishwasherStatusSchema = zod.z.object({
76544
76657
  availability: DishwasherAvailabilitySchema,
76545
76658
  /** Coarse state for compact surfaces; `unavailable` whenever the appliance state is not known. */
@@ -76572,8 +76685,6 @@ var require_dist_QyW9hVO2 = __commonJS({
76572
76685
  powerOn: zod.z.boolean().nullable(),
76573
76686
  /** The raw power state (`On`, `Off`, `Standby`, …); null when not known. */
76574
76687
  powerState: zod.z.string().nullable(),
76575
- saltLevel: DishwasherConsumableLevelSchema.nullable(),
76576
- rinseAidLevel: DishwasherConsumableLevelSchema.nullable(),
76577
76688
  /**
76578
76689
  * True while the appliance asks for its filter to be cleaned (smart-filter, filter-system or
76579
76690
  * combined care-and-filter reminder raised); false when every such reminder reads clear; null
@@ -84900,16 +85011,6 @@ var require_dist_QyW9hVO2 = __commonJS({
84900
85011
  "drying",
84901
85012
  "error"
84902
85013
  ]);
84903
- var TankStatusSchema = zod.z.object({
84904
- /** Numeric fill 0..100 when the hardware reports a percentage; null otherwise. */
84905
- level: zod.z.number().min(0).max(100).nullable(),
84906
- /** Discrete state when the hardware is binary-mode; null otherwise. */
84907
- status: zod.z.enum([
84908
- "ok",
84909
- "low",
84910
- "full"
84911
- ]).nullable()
84912
- });
84913
85014
  var VacuumControlStatusSchema = zod.z.object({
84914
85015
  /** Lifecycle state of the vacuum. */
84915
85016
  state: VacuumStateSchema,
@@ -84921,14 +85022,6 @@ var require_dist_QyW9hVO2 = __commonJS({
84921
85022
  fanSpeed: zod.z.string().nullable(),
84922
85023
  /** Speed tokens the hardware accepts — drives the UI selector. */
84923
85024
  availableFanSpeeds: zod.z.array(zod.z.string()),
84924
- /** Clean-water (mop) tank. Null when the hardware has no clean-water tank. */
84925
- cleanWater: TankStatusSchema.nullable(),
84926
- /** Dirty-water (recovery) tank. Null when the hardware has no dirty-water tank. */
84927
- dirtyWater: TankStatusSchema.nullable(),
84928
- /** Detergent tank. Null when the hardware has no detergent tank. */
84929
- detergent: TankStatusSchema.nullable(),
84930
- /** Dust bin. Null when the hardware has no dust bin. */
84931
- dustBin: TankStatusSchema.nullable(),
84932
85025
  /** 0..100 cleaning-completion percentage of the current task, or null. */
84933
85026
  progressPercent: zod.z.number().min(0).max(100).nullable(),
84934
85027
  /** Current error code (0 / null = no error). */
@@ -84990,35 +85083,14 @@ var require_dist_QyW9hVO2 = __commonJS({
84990
85083
  */
84991
85084
  durability: "session",
84992
85085
  /**
84993
- * History (D850): recorded series. Tank levels are fixed-object paths (`cleanWater.level`, ...).
85086
+ * History (D850): recorded series. The tanks moved to `consumables` (D915), whose
85087
+ * items are keyed by runtime ids and record no history.
84994
85088
  */
84995
- history: { fields: {
84996
- batteryLevel: {
84997
- kind: "gauge",
84998
- unit: "%",
84999
- deadband: 2
85000
- },
85001
- "cleanWater.level": {
85002
- kind: "gauge",
85003
- unit: "%",
85004
- deadband: 2
85005
- },
85006
- "dirtyWater.level": {
85007
- kind: "gauge",
85008
- unit: "%",
85009
- deadband: 2
85010
- },
85011
- "detergent.level": {
85012
- kind: "gauge",
85013
- unit: "%",
85014
- deadband: 2
85015
- },
85016
- "dustBin.level": {
85017
- kind: "gauge",
85018
- unit: "%",
85019
- deadband: 2
85020
- }
85021
- } }
85089
+ history: { fields: { batteryLevel: {
85090
+ kind: "gauge",
85091
+ unit: "%",
85092
+ deadband: 2
85093
+ } } }
85022
85094
  };
85023
85095
  var ValveStateSchema = zod.z.enum([
85024
85096
  "open",
@@ -89313,6 +89385,12 @@ var require_dist_QyW9hVO2 = __commonJS({
89313
89385
  addonId: null,
89314
89386
  access: "create"
89315
89387
  },
89388
+ "deviceHistory.deleteOrphanSeries": {
89389
+ capName: "device-history",
89390
+ capScope: "system",
89391
+ addonId: null,
89392
+ access: "delete"
89393
+ },
89316
89394
  "deviceHistory.getDeviceEnabled": {
89317
89395
  capName: "device-history",
89318
89396
  capScope: "system",
@@ -89331,6 +89409,12 @@ var require_dist_QyW9hVO2 = __commonJS({
89331
89409
  addonId: null,
89332
89410
  access: "view"
89333
89411
  },
89412
+ "deviceHistory.listOrphanSeries": {
89413
+ capName: "device-history",
89414
+ capScope: "system",
89415
+ addonId: null,
89416
+ access: "view"
89417
+ },
89334
89418
  "deviceHistory.listSeries": {
89335
89419
  capName: "device-history",
89336
89420
  capScope: "system",
@@ -99625,33 +99709,11 @@ var require_dist_QyW9hVO2 = __commonJS({
99625
99709
  unit: "\xB0C",
99626
99710
  deadband: 0.1
99627
99711
  } },
99628
- "vacuum-control": {
99629
- "batteryLevel": {
99630
- kind: "gauge",
99631
- unit: "%",
99632
- deadband: 2
99633
- },
99634
- "cleanWater.level": {
99635
- kind: "gauge",
99636
- unit: "%",
99637
- deadband: 2
99638
- },
99639
- "dirtyWater.level": {
99640
- kind: "gauge",
99641
- unit: "%",
99642
- deadband: 2
99643
- },
99644
- "detergent.level": {
99645
- kind: "gauge",
99646
- unit: "%",
99647
- deadband: 2
99648
- },
99649
- "dustBin.level": {
99650
- kind: "gauge",
99651
- unit: "%",
99652
- deadband: 2
99653
- }
99654
- },
99712
+ "vacuum-control": { "batteryLevel": {
99713
+ kind: "gauge",
99714
+ unit: "%",
99715
+ deadband: 2
99716
+ } },
99655
99717
  "valve": { "position": {
99656
99718
  kind: "gauge",
99657
99719
  unit: "%",
@@ -99693,6 +99755,36 @@ var require_dist_QyW9hVO2 = __commonJS({
99693
99755
  }
99694
99756
  }
99695
99757
  };
99758
+ var HISTORY_ITEM_CATALOG = { "consumables": {
99759
+ path: "items",
99760
+ key: "key",
99761
+ label: "label",
99762
+ fields: {
99763
+ "level": {
99764
+ kind: "gauge",
99765
+ unit: "%",
99766
+ deadband: 2,
99767
+ displayName: "Level"
99768
+ },
99769
+ "status": {
99770
+ kind: "enum",
99771
+ displayName: "Status",
99772
+ values: [
99773
+ "ok",
99774
+ "low",
99775
+ "empty",
99776
+ "full",
99777
+ "replace"
99778
+ ]
99779
+ },
99780
+ "remaining.value": {
99781
+ kind: "gauge",
99782
+ unitFrom: "remaining.unit",
99783
+ deadband: 1,
99784
+ displayName: "Remaining"
99785
+ }
99786
+ }
99787
+ } };
99696
99788
  var G711_SCALE_CORRECTION_DB = {
99697
99789
  PCMU: 20 * Math.log10(4),
99698
99790
  PCMA: 20 * Math.log10(8)
@@ -100179,7 +100271,7 @@ var require_dist_QyW9hVO2 = __commonJS({
100179
100271
  ([...RULE_TEXT], [...COMBINED_ALARM]),
100180
100272
  ([...RULE_TEXT], [...DEVICE_EVENTS]),
100181
100273
  ([...RULE_TEXT], [...BATTERY, ...CONSUMABLE]),
100182
- ([...RULE_TEXT], [...CONSUMABLE]),
100274
+ ([...RULE_TEXT], [...CONSUMABLE, ...TANK]),
100183
100275
  ([...RULE_TEXT], [...TANK]),
100184
100276
  [...RULE_TEXT],
100185
100277
  ([...RULE_TEXT], [...UPDATES, ...ALERT_EPISODE]),
@@ -101264,6 +101356,12 @@ var require_dist_QyW9hVO2 = __commonJS({
101264
101356
  return HISTORY_DEFAULT_MIN_INTERVAL_MS;
101265
101357
  }
101266
101358
  });
101359
+ Object.defineProperty(exports, "HISTORY_ITEM_CATALOG", {
101360
+ enumerable: true,
101361
+ get: function() {
101362
+ return HISTORY_ITEM_CATALOG;
101363
+ }
101364
+ });
101267
101365
  Object.defineProperty(exports, "HUB_MDNS_SERVICE_TYPE", {
101268
101366
  enumerable: true,
101269
101367
  get: function() {
@@ -102086,6 +102184,12 @@ var require_dist_QyW9hVO2 = __commonJS({
102086
102184
  return gbToBytes;
102087
102185
  }
102088
102186
  });
102187
+ Object.defineProperty(exports, "historyItemFieldPath", {
102188
+ enumerable: true,
102189
+ get: function() {
102190
+ return historyItemFieldPath;
102191
+ }
102192
+ });
102089
102193
  Object.defineProperty(exports, "hydrateSchema", {
102090
102194
  enumerable: true,
102091
102195
  get: function() {
@@ -114184,11 +114288,11 @@ ${values.join("\n")}` : `${blockName} :`;
114184
114288
  }
114185
114289
  });
114186
114290
 
114187
- // ../system/dist/device-ca-DpvQ5wrb.js
114188
- var require_device_ca_DpvQ5wrb = __commonJS({
114189
- "../system/dist/device-ca-DpvQ5wrb.js"(exports) {
114291
+ // ../system/dist/device-ca-CC58lmNV.js
114292
+ var require_device_ca_CC58lmNV = __commonJS({
114293
+ "../system/dist/device-ca-CC58lmNV.js"(exports) {
114190
114294
  "use strict";
114191
- var require_dist10 = require_dist_QyW9hVO2();
114295
+ var require_dist10 = require_dist_DuBtzSCh();
114192
114296
  var require_x509_es = require_x509_es_C4cOo48M();
114193
114297
  var node_crypto = __require("crypto");
114194
114298
  var SYSTEM_SECRET_OWNER = "system";
@@ -114646,7 +114750,7 @@ var require_alerts_addon = __commonJS({
114646
114750
  [Symbol.toStringTag]: { value: "Module" }
114647
114751
  });
114648
114752
  require_chunk_Cek0wNdY();
114649
- var require_dist10 = require_dist_QyW9hVO2();
114753
+ var require_dist10 = require_dist_DuBtzSCh();
114650
114754
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
114651
114755
  function nextEpisodeId(input) {
114652
114756
  const { previous } = input;
@@ -116114,7 +116218,7 @@ var require_calendars_addon = __commonJS({
116114
116218
  [Symbol.toStringTag]: { value: "Module" }
116115
116219
  });
116116
116220
  require_chunk_Cek0wNdY();
116117
- var require_dist10 = require_dist_QyW9hVO2();
116221
+ var require_dist10 = require_dist_DuBtzSCh();
116118
116222
  var require_settle_sources = require_settle_sources_Bhsy57y();
116119
116223
  var zod = require_zod();
116120
116224
  var CALENDAR_COLOR_PALETTE = [
@@ -116330,11 +116434,11 @@ var require_calendars = __commonJS({
116330
116434
  }
116331
116435
  });
116332
116436
 
116333
- // ../system/dist/composition-sources-DqHwltDV.js
116334
- var require_composition_sources_DqHwltDV = __commonJS({
116335
- "../system/dist/composition-sources-DqHwltDV.js"(exports) {
116437
+ // ../system/dist/composition-sources-BGXTiUZm.js
116438
+ var require_composition_sources_BGXTiUZm = __commonJS({
116439
+ "../system/dist/composition-sources-BGXTiUZm.js"(exports) {
116336
116440
  "use strict";
116337
- var require_dist10 = require_dist_QyW9hVO2();
116441
+ var require_dist10 = require_dist_DuBtzSCh();
116338
116442
  function normalizedNativeOwner(owner) {
116339
116443
  const trimmed = owner?.trim();
116340
116444
  return trimmed ? trimmed : null;
@@ -116449,8 +116553,8 @@ var require_composer_addon = __commonJS({
116449
116553
  [Symbol.toStringTag]: { value: "Module" }
116450
116554
  });
116451
116555
  require_chunk_Cek0wNdY();
116452
- var require_dist10 = require_dist_QyW9hVO2();
116453
- var require_composition_sources = require_composition_sources_DqHwltDV();
116556
+ var require_dist10 = require_dist_DuBtzSCh();
116557
+ var require_composition_sources = require_composition_sources_BGXTiUZm();
116454
116558
  var zod = require_zod();
116455
116559
  function isRecord$2(value) {
116456
116560
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -120260,7 +120364,7 @@ var require_console_logging = __commonJS({
120260
120364
  [Symbol.toStringTag]: { value: "Module" }
120261
120365
  });
120262
120366
  require_chunk_Cek0wNdY();
120263
- var require_dist10 = require_dist_QyW9hVO2();
120367
+ var require_dist10 = require_dist_DuBtzSCh();
120264
120368
  var require_formatter = require_formatter_DqAKDlvN();
120265
120369
  var LEVEL_RANK = {
120266
120370
  debug: 0,
@@ -124924,9 +125028,9 @@ var require_core_blocks_addon = __commonJS({
124924
125028
  "use strict";
124925
125029
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
124926
125030
  var require_chunk = require_chunk_Cek0wNdY();
124927
- var require_dist10 = require_dist_QyW9hVO2();
125031
+ var require_dist10 = require_dist_DuBtzSCh();
124928
125032
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
124929
- var require_composition_sources = require_composition_sources_DqHwltDV();
125033
+ var require_composition_sources = require_composition_sources_BGXTiUZm();
124930
125034
  var require_prefix_range = require_prefix_range_D_KueXbQ();
124931
125035
  var require_prefix_walk = require_prefix_walk_Dv6MLZlM();
124932
125036
  var zod = require_zod();
@@ -127333,9 +127437,9 @@ var require_event_category_7AwNbPjm = __commonJS({
127333
127437
  }
127334
127438
  });
127335
127439
 
127336
- // ../types/dist/sleep-CD18m9HF.js
127337
- var require_sleep_CD18m9HF = __commonJS({
127338
- "../types/dist/sleep-CD18m9HF.js"(exports) {
127440
+ // ../types/dist/sleep-DVovpDRU.js
127441
+ var require_sleep_DVovpDRU = __commonJS({
127442
+ "../types/dist/sleep-DVovpDRU.js"(exports) {
127339
127443
  "use strict";
127340
127444
  var require_event_category = require_event_category_7AwNbPjm();
127341
127445
  var zod = require_zod();
@@ -132469,6 +132573,9 @@ var require_sleep_CD18m9HF = __commonJS({
132469
132573
  kbps: "kbps",
132470
132574
  fps: "fps"
132471
132575
  };
132576
+ function historyItemFieldPath(itemsPath, field) {
132577
+ return `${itemsPath}[].${field}`;
132578
+ }
132472
132579
  var HISTORY_DEFAULT_MIN_INTERVAL_MS = 1e4;
132473
132580
  function resolveCapMount(def) {
132474
132581
  if (def.mount) return def.mount;
@@ -132491,7 +132598,11 @@ var require_sleep_CD18m9HF = __commonJS({
132491
132598
  zod.z.literal(4)
132492
132599
  ]).optional(),
132493
132600
  tab: zod.z.string().optional(),
132494
- location: zod.z.enum(["settings", "top-tab"]).optional(),
132601
+ location: zod.z.enum([
132602
+ "settings",
132603
+ "top-tab",
132604
+ "hero"
132605
+ ]).optional(),
132495
132606
  order: zod.z.number().optional(),
132496
132607
  fields: zod.z.array(zod.z.any())
132497
132608
  });
@@ -135089,6 +135200,12 @@ var require_sleep_CD18m9HF = __commonJS({
135089
135200
  return hfModelUrl;
135090
135201
  }
135091
135202
  });
135203
+ Object.defineProperty(exports, "historyItemFieldPath", {
135204
+ enumerable: true,
135205
+ get: function() {
135206
+ return historyItemFieldPath;
135207
+ }
135208
+ });
135092
135209
  Object.defineProperty(exports, "hydrateSchema", {
135093
135210
  enumerable: true,
135094
135211
  get: function() {
@@ -135493,7 +135610,7 @@ var require_addon = __commonJS({
135493
135610
  "../types/dist/addon.js"(exports) {
135494
135611
  "use strict";
135495
135612
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
135496
- var require_sleep = require_sleep_CD18m9HF();
135613
+ var require_sleep = require_sleep_DVovpDRU();
135497
135614
  var require_event_category = require_event_category_7AwNbPjm();
135498
135615
  var require_err_msg = require_err_msg_BhVbr0NX();
135499
135616
  var CAP_INPUT_DEFAULTS = Object.freeze({
@@ -136492,24 +136609,6 @@ var require_addon = __commonJS({
136492
136609
  "path": "powerState",
136493
136610
  "valueType": "string"
136494
136611
  },
136495
- {
136496
- "path": "saltLevel",
136497
- "valueType": "enum",
136498
- "enumValues": [
136499
- "ok",
136500
- "low",
136501
- "empty"
136502
- ]
136503
- },
136504
- {
136505
- "path": "rinseAidLevel",
136506
- "valueType": "enum",
136507
- "enumValues": [
136508
- "ok",
136509
- "low",
136510
- "empty"
136511
- ]
136512
- },
136513
136612
  {
136514
136613
  "path": "filterCleaningDue",
136515
136614
  "valueType": "boolean"
@@ -137535,58 +137634,6 @@ var require_addon = __commonJS({
137535
137634
  "path": "fanSpeed",
137536
137635
  "valueType": "string"
137537
137636
  },
137538
- {
137539
- "path": "cleanWater.level",
137540
- "valueType": "number"
137541
- },
137542
- {
137543
- "path": "cleanWater.status",
137544
- "valueType": "enum",
137545
- "enumValues": [
137546
- "ok",
137547
- "low",
137548
- "full"
137549
- ]
137550
- },
137551
- {
137552
- "path": "dirtyWater.level",
137553
- "valueType": "number"
137554
- },
137555
- {
137556
- "path": "dirtyWater.status",
137557
- "valueType": "enum",
137558
- "enumValues": [
137559
- "ok",
137560
- "low",
137561
- "full"
137562
- ]
137563
- },
137564
- {
137565
- "path": "detergent.level",
137566
- "valueType": "number"
137567
- },
137568
- {
137569
- "path": "detergent.status",
137570
- "valueType": "enum",
137571
- "enumValues": [
137572
- "ok",
137573
- "low",
137574
- "full"
137575
- ]
137576
- },
137577
- {
137578
- "path": "dustBin.level",
137579
- "valueType": "number"
137580
- },
137581
- {
137582
- "path": "dustBin.status",
137583
- "valueType": "enum",
137584
- "enumValues": [
137585
- "ok",
137586
- "low",
137587
- "full"
137588
- ]
137589
- },
137590
137637
  {
137591
137638
  "path": "progressPercent",
137592
137639
  "valueType": "number"
@@ -138151,7 +138198,7 @@ var require_device_manager_addon = __commonJS({
138151
138198
  [Symbol.toStringTag]: { value: "Module" }
138152
138199
  });
138153
138200
  require_chunk_Cek0wNdY();
138154
- var require_dist10 = require_dist_QyW9hVO2();
138201
+ var require_dist10 = require_dist_DuBtzSCh();
138155
138202
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
138156
138203
  var require_prefix_range = require_prefix_range_D_KueXbQ();
138157
138204
  var require_prefix_walk = require_prefix_walk_Dv6MLZlM();
@@ -141089,7 +141136,7 @@ var require_device_manager_addon = __commonJS({
141089
141136
  title: ui.label,
141090
141137
  tab: ui.tab,
141091
141138
  order: ui.order ?? 0,
141092
- ...ui.topTab ? { location: "top-tab" } : {},
141139
+ ...ui.placement === "hero" ? { location: "hero" } : ui.topTab ? { location: "top-tab" } : {},
141093
141140
  fields: [{
141094
141141
  type: "widget",
141095
141142
  key: `${entry.capName}Widget`,
@@ -147223,7 +147270,7 @@ var require_hub_forwarder = __commonJS({
147223
147270
  [Symbol.toStringTag]: { value: "Module" }
147224
147271
  });
147225
147272
  require_chunk_Cek0wNdY();
147226
- var require_dist10 = require_dist_QyW9hVO2();
147273
+ var require_dist10 = require_dist_DuBtzSCh();
147227
147274
  var require_formatter = require_formatter_DqAKDlvN();
147228
147275
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
147229
147276
  var HubForwarderDestination = class {
@@ -147357,7 +147404,7 @@ var require_liveness_monitor_addon = __commonJS({
147357
147404
  "use strict";
147358
147405
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
147359
147406
  require_chunk_Cek0wNdY();
147360
- var require_dist10 = require_dist_QyW9hVO2();
147407
+ var require_dist10 = require_dist_DuBtzSCh();
147361
147408
  var NO_DEVICES = "liveness:no-devices";
147362
147409
  var ALL_OFFLINE = "liveness:all-devices-offline";
147363
147410
  var NO_FOOTAGE_PREFIX = "liveness:no-footage:";
@@ -147720,10 +147767,10 @@ var require_local_auth_addon = __commonJS({
147720
147767
  [Symbol.toStringTag]: { value: "Module" }
147721
147768
  });
147722
147769
  var require_chunk = require_chunk_Cek0wNdY();
147723
- var require_dist10 = require_dist_QyW9hVO2();
147770
+ var require_dist10 = require_dist_DuBtzSCh();
147724
147771
  var require_safe_buffer$1 = require_safe_buffer_D3aoa_7X();
147725
147772
  var require_ms$1 = require_ms_LB3giGoU();
147726
- var require_device_ca = require_device_ca_DpvQ5wrb();
147773
+ var require_device_ca = require_device_ca_CC58lmNV();
147727
147774
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
147728
147775
  var zod = require_zod();
147729
147776
  var node_crypto = __require("crypto");
@@ -158111,7 +158158,7 @@ var require_loki_logging = __commonJS({
158111
158158
  [Symbol.toStringTag]: { value: "Module" }
158112
158159
  });
158113
158160
  require_chunk_Cek0wNdY();
158114
- var require_dist10 = require_dist_QyW9hVO2();
158161
+ var require_dist10 = require_dist_DuBtzSCh();
158115
158162
  var require_log_read_filter = require_log_read_filter_Bi066jwU();
158116
158163
  var zod = require_zod();
158117
158164
  function sanitizeLabelName(raw) {
@@ -159072,7 +159119,7 @@ var require_native_metrics_addon = __commonJS({
159072
159119
  [Symbol.toStringTag]: { value: "Module" }
159073
159120
  });
159074
159121
  var require_chunk = require_chunk_Cek0wNdY();
159075
- var require_dist10 = require_dist_QyW9hVO2();
159122
+ var require_dist10 = require_dist_DuBtzSCh();
159076
159123
  var node_fs_promises = __require("fs/promises");
159077
159124
  var node_util = __require("util");
159078
159125
  var node_os = __require("os");
@@ -161639,7 +161686,7 @@ var require_device_history_addon = __commonJS({
161639
161686
  [Symbol.toStringTag]: { value: "Module" }
161640
161687
  });
161641
161688
  require_chunk_Cek0wNdY();
161642
- var require_dist10 = require_dist_QyW9hVO2();
161689
+ var require_dist10 = require_dist_DuBtzSCh();
161643
161690
  var FIVE_MINUTES_MS = 5 * 6e4;
161644
161691
  var HOUR_MS = 60 * 6e4;
161645
161692
  var DAY_MS = 24 * HOUR_MS;
@@ -161804,13 +161851,25 @@ var require_device_history_addon = __commonJS({
161804
161851
  }
161805
161852
  return deleted;
161806
161853
  }
161807
- /** Tier by tier in slices, then the series row (and whatever the slices missed). */
161854
+ /**
161855
+ * Tier by tier in slices, then the series row (and whatever the slices missed).
161856
+ * Answers the rows the slices deleted per tier. The caller removes the series
161857
+ * from the registry, which releases its per-series state.
161858
+ */
161808
161859
  async purgeSeries(series, now) {
161860
+ const deleted = {
161861
+ raw: 0,
161862
+ "5m": 0,
161863
+ "1h": 0,
161864
+ "1d": 0
161865
+ };
161809
161866
  for (const tier of TIERS) {
161810
161867
  const lo = tier === "raw" || tier === "5m" ? Math.max(series.firstTs - BUCKET_LEAD_MS, now - SHORT_TIER_LOOKBACK_MS) : series.firstTs - BUCKET_LEAD_MS;
161811
- await this.deleteSliced(tier, series.id, Math.max(0, lo), now + DAY_MS);
161868
+ deleted[tier] = await this.deleteSliced(tier, series.id, Math.max(0, lo), now + DAY_MS);
161869
+ this.deps.counters.addDeleted(tier, deleted[tier]);
161812
161870
  }
161813
161871
  await this.deps.store.deleteSeries(series.id);
161872
+ return deleted;
161814
161873
  }
161815
161874
  /**
161816
161875
  * Delete every series of a DELETED device now (D865). Driven by a tombstone
@@ -162135,13 +162194,13 @@ var require_device_history_addon = __commonJS({
162135
162194
  }
162136
162195
  }
162137
162196
  };
162138
- function isRecord$1(value) {
162197
+ function isRecord$2(value) {
162139
162198
  return typeof value === "object" && value !== null && !Array.isArray(value);
162140
162199
  }
162141
162200
  function readPath(slice, path) {
162142
162201
  let current = slice;
162143
162202
  for (const step of path.split(".")) {
162144
- if (!isRecord$1(current)) return void 0;
162203
+ if (!isRecord$2(current)) return void 0;
162145
162204
  current = current[step];
162146
162205
  }
162147
162206
  return current;
@@ -162163,13 +162222,16 @@ var require_device_history_addon = __commonJS({
162163
162222
  function capSeriesKey(capName, fieldPath) {
162164
162223
  return `${CAP_KEY_PREFIX}${capName}.${fieldPath}`;
162165
162224
  }
162225
+ function capItemSeriesKey(capName, items, field) {
162226
+ return capSeriesKey(capName, require_dist10.historyItemFieldPath(items.path, field));
162227
+ }
162166
162228
  function humanize(capName, fieldPath) {
162167
162229
  const field = fieldPath.replace(/([A-Z])/g, " $1").replace(/\./g, " ").toLowerCase();
162168
162230
  return `${capName.replace(/-/g, " ")} ${field}`;
162169
162231
  }
162170
- function catalogMetrics(catalog, now) {
162171
- return Object.entries(catalog).flatMap(([capName, fields]) => Object.entries(fields).map(([path, spec]) => ({
162172
- key: capSeriesKey(capName, path),
162232
+ function metricOf(key, capName, path, spec, now) {
162233
+ return {
162234
+ key,
162173
162235
  source: "cap",
162174
162236
  ownerAddon: "device-history",
162175
162237
  subjectKind: "device",
@@ -162179,7 +162241,15 @@ var require_device_history_addon = __commonJS({
162179
162241
  ingest: "points",
162180
162242
  declaredAt: now,
162181
162243
  values: spec.kind === "enum" ? spec.values ?? [] : null
162182
- })));
162244
+ };
162245
+ }
162246
+ function catalogMetrics(catalog, itemCatalog, now) {
162247
+ const fields = Object.entries(catalog).flatMap(([capName, specs]) => Object.entries(specs).map(([path, spec]) => metricOf(capSeriesKey(capName, path), capName, path, spec, now)));
162248
+ const items = Object.entries(itemCatalog).flatMap(([capName, block]) => Object.entries(block.fields).map(([field, spec]) => metricOf(capItemSeriesKey(capName, block, field), capName, field, spec, now)));
162249
+ return [...fields, ...items];
162250
+ }
162251
+ function isRecord$1(value) {
162252
+ return typeof value === "object" && value !== null && !Array.isArray(value);
162183
162253
  }
162184
162254
  var MAX_PARKED_DEVICES = 5e3;
162185
162255
  var SEED_CONCURRENCY = 8;
@@ -162204,7 +162274,8 @@ var require_device_history_addon = __commonJS({
162204
162274
  /** One `DeviceStateChanged`. Synchronous: the bus must never wait on us. */
162205
162275
  handleState(deviceId, capName, slice, at) {
162206
162276
  const fields = this.deps.catalog[capName];
162207
- if (fields === void 0) return;
162277
+ const items = this.deps.itemCatalog[capName];
162278
+ if (fields === void 0 && items === void 0) return;
162208
162279
  const { counters, directory } = this.deps;
162209
162280
  counters.eventsSeen += 1;
162210
162281
  if (this.deps.optedOut().has(deviceId)) {
@@ -162220,14 +162291,46 @@ var require_device_history_addon = __commonJS({
162220
162291
  counters.skip("not-a-real-device");
162221
162292
  return;
162222
162293
  }
162223
- for (const [path, spec] of Object.entries(fields)) {
162294
+ this.captureSlice(deviceId, type, capName, slice, at, false);
162295
+ }
162296
+ /** Every catalogued field and keyed item of one cap slice, for a real device. */
162297
+ captureSlice(deviceId, type, capName, slice, at, reconciling) {
162298
+ for (const [path, spec] of Object.entries(this.deps.catalog[capName] ?? {})) {
162224
162299
  if (spec.excludeDeviceTypes?.includes(type) === true) {
162225
- counters.skip("excluded-device-type");
162300
+ if (!reconciling) this.deps.counters.skip("excluded-device-type");
162226
162301
  continue;
162227
162302
  }
162228
- const value = this.readValue(slice, capName, path, spec);
162303
+ const key = capSeriesKey(capName, path);
162304
+ const value = this.readValue(slice, key, path, spec);
162229
162305
  if (value === null) continue;
162230
- this.consider(deviceId, capName, path, spec, value, at, false);
162306
+ this.consider(deviceId, key, "", spec, value, at, reconciling);
162307
+ }
162308
+ const items = this.deps.itemCatalog[capName];
162309
+ if (items !== void 0) this.captureItems(deviceId, type, capName, items, slice, at, reconciling);
162310
+ }
162311
+ /**
162312
+ * A keyed array (D917): each item with a usable key contributes its fields
162313
+ * under that key as the series scope. A value that is not an array is no
162314
+ * reading; an item without a string key cannot be followed and is counted.
162315
+ */
162316
+ captureItems(deviceId, type, capName, items, slice, at, reconciling) {
162317
+ const list = readPath(slice, items.path);
162318
+ if (!Array.isArray(list)) return;
162319
+ const { counters } = this.deps;
162320
+ if (list.length > 64) counters.skip("item-overflow", list.length - 64);
162321
+ for (const entry of list.slice(0, 64)) {
162322
+ const scope = isRecord$1(entry) ? entry[items.key] : void 0;
162323
+ if (!isRecord$1(entry) || typeof scope !== "string" || scope.length === 0 || scope.length > 128) {
162324
+ counters.skip("item-key-invalid");
162325
+ continue;
162326
+ }
162327
+ for (const [field, spec] of Object.entries(items.fields)) {
162328
+ if (spec.excludeDeviceTypes?.includes(type) === true) continue;
162329
+ const key = capItemSeriesKey(capName, items, field);
162330
+ const value = this.readValue(entry, key, field, spec);
162331
+ if (value === null) continue;
162332
+ this.consider(deviceId, key, scope, spec, value, at, reconciling);
162333
+ }
162231
162334
  }
162232
162335
  }
162233
162336
  /**
@@ -162235,7 +162338,7 @@ var require_device_history_addon = __commonJS({
162235
162338
  * `enum` state becomes the ordinal of its value in the metric's list; a value
162236
162339
  * the full list cannot take is refused and counted (`enum-overflow`).
162237
162340
  */
162238
- readValue(slice, capName, path, spec) {
162341
+ readValue(slice, key, path, spec) {
162239
162342
  if (spec.skipWhenSet !== void 0) {
162240
162343
  const gate = readPath(slice, spec.skipWhenSet);
162241
162344
  if (gate !== void 0 && gate !== null) return null;
@@ -162243,20 +162346,19 @@ var require_device_history_addon = __commonJS({
162243
162346
  if (spec.kind !== "enum") return readNumeric(slice, path, spec.kind);
162244
162347
  const raw = readPath(slice, path);
162245
162348
  if (typeof raw !== "string" || raw.length === 0) return null;
162246
- const ordinal = this.deps.registry.ordinalOf(capSeriesKey(capName, path), raw);
162349
+ const ordinal = this.deps.registry.ordinalOf(key, raw);
162247
162350
  if (ordinal === null) this.deps.counters.skip("enum-overflow");
162248
162351
  return ordinal;
162249
162352
  }
162250
- consider(deviceId, capName, path, spec, value, at, reconciling) {
162251
- const key = capSeriesKey(capName, path);
162353
+ consider(deviceId, key, scope, spec, value, at, reconciling) {
162252
162354
  const subject = {
162253
162355
  kind: "device",
162254
162356
  deviceId
162255
162357
  };
162256
- const identity = identityOf(key, `d:${deviceId}`, "");
162358
+ const identity = identityOf(key, `d:${deviceId}`, scope);
162257
162359
  const decision = decideCapture(spec, this.recorded.get(identity) ?? null, value, at, { ignoreFloor: reconciling });
162258
162360
  if (decision.action === "emit") {
162259
- this.emit(identity, key, subject, value, at);
162361
+ this.emit(identity, key, subject, scope, value, at);
162260
162362
  if (reconciling) this.deps.counters.reconciledPoints += 1;
162261
162363
  return;
162262
162364
  }
@@ -162264,6 +162366,7 @@ var require_device_history_addon = __commonJS({
162264
162366
  this.pending.set(identity, {
162265
162367
  key,
162266
162368
  subject,
162369
+ scope,
162267
162370
  spec,
162268
162371
  value
162269
162372
  });
@@ -162272,7 +162375,7 @@ var require_device_history_addon = __commonJS({
162272
162375
  this.pending.delete(identity);
162273
162376
  this.deps.counters.skip(decision.reason);
162274
162377
  }
162275
- emit(identity, key, subject, value, ts) {
162378
+ emit(identity, key, subject, scope, value, ts) {
162276
162379
  this.recorded.set(identity, {
162277
162380
  value,
162278
162381
  ts
@@ -162282,7 +162385,7 @@ var require_device_history_addon = __commonJS({
162282
162385
  this.deps.writer.enqueue({
162283
162386
  key,
162284
162387
  subject,
162285
- scope: "",
162388
+ scope,
162286
162389
  ts,
162287
162390
  value
162288
162391
  });
@@ -162299,7 +162402,7 @@ var require_device_history_addon = __commonJS({
162299
162402
  }
162300
162403
  const notBefore = (recorded?.ts ?? now) + minIntervalOf(p.spec);
162301
162404
  if (now < notBefore) continue;
162302
- this.emit(identity, p.key, p.subject, p.value, notBefore);
162405
+ this.emit(identity, p.key, p.subject, p.scope, p.value, notBefore);
162303
162406
  this.deps.counters.trailingEmitted += 1;
162304
162407
  }
162305
162408
  }
@@ -162347,16 +162450,7 @@ var require_device_history_addon = __commonJS({
162347
162450
  if (!Number.isInteger(deviceId) || optedOut.has(deviceId)) continue;
162348
162451
  const type = this.deps.directory.typeOf(deviceId);
162349
162452
  if (type === void 0 || !isRealDeviceType(type)) continue;
162350
- for (const [capName, fields] of Object.entries(this.deps.catalog)) {
162351
- const slice = caps[capName];
162352
- if (slice === void 0) continue;
162353
- for (const [path, spec] of Object.entries(fields)) {
162354
- if (spec.excludeDeviceTypes?.includes(type) === true) continue;
162355
- const value = this.readValue(slice, capName, path, spec);
162356
- if (value === null) continue;
162357
- this.consider(deviceId, capName, path, spec, value, now, true);
162358
- }
162359
- }
162453
+ for (const [capName, slice] of Object.entries(caps)) this.captureSlice(deviceId, type, capName, slice, now, true);
162360
162454
  }
162361
162455
  }
162362
162456
  /**
@@ -162382,8 +162476,8 @@ var require_device_history_addon = __commonJS({
162382
162476
  return this.pending.size;
162383
162477
  }
162384
162478
  /** The last recorded state of a series, for specs. */
162385
- lastRecorded(key, subjectId) {
162386
- return this.recorded.get(identityOf(key, subjectId, ""));
162479
+ lastRecorded(key, subjectId, scope = "") {
162480
+ return this.recorded.get(identityOf(key, subjectId, scope));
162387
162481
  }
162388
162482
  };
162389
162483
  var FLUSH_INTERVAL_MS = 1e4;
@@ -162802,8 +162896,10 @@ var require_device_history_addon = __commonJS({
162802
162896
  }
162803
162897
  var HistoryQuery = class {
162804
162898
  deps;
162899
+ itemFields;
162805
162900
  constructor(deps) {
162806
162901
  this.deps = deps;
162902
+ this.itemFields = itemFieldsByKey(deps.itemCatalog);
162807
162903
  }
162808
162904
  async query(input) {
162809
162905
  if (input.key === "derived:energy.kwh") return this.queryEnergy(input);
@@ -162991,12 +163087,14 @@ var require_device_history_addon = __commonJS({
162991
163087
  for (const s of registry.forSubject(subjectIdOf(subject))) {
162992
163088
  const metric = registry.metric(s.key);
162993
163089
  if (metric === void 0) continue;
163090
+ const item = this.itemFields.get(s.key);
163091
+ const named = item === void 0 || subject.kind !== "device" ? null : await this.itemNaming(subject.deviceId, item, s.scope, metric.unit, slices);
162994
163092
  infos.push({
162995
163093
  key: s.key,
162996
163094
  scope: s.scope,
162997
163095
  kind: metric.kind,
162998
- unit: await this.unitOf(subject, s, metric.unit, slices),
162999
- displayName: metric.displayName,
163096
+ unit: named !== null ? named.unit : await this.unitOf(subject, s, metric.unit, slices),
163097
+ displayName: named !== null ? `${named.label} \xB7 ${metric.displayName}` : metric.displayName,
163000
163098
  source: metric.source,
163001
163099
  values: metric.kind === "enum" ? metric.values ?? [] : null,
163002
163100
  firstTs: s.firstTs,
@@ -163017,15 +163115,38 @@ var require_device_history_addon = __commonJS({
163017
163115
  });
163018
163116
  return infos.toSorted((a, b) => a.key.localeCompare(b.key) || a.scope.localeCompare(b.scope));
163019
163117
  }
163118
+ /**
163119
+ * An item series (D917) is named after its item and takes its `unitFrom`
163120
+ * from it, both read from the device's CURRENT slice: the item's `label`,
163121
+ * else its key (the scope) when the item is gone or the slice unreadable.
163122
+ */
163123
+ async itemNaming(deviceId, item, scope, fixed, slices) {
163124
+ const slice = await this.sliceOf(deviceId, item.capName, slices);
163125
+ const list = slice === null ? void 0 : readPath(slice, item.items.path);
163126
+ const entry = Array.isArray(list) ? list.find((e) => isObject(e) && e[item.items.key] === scope) : void 0;
163127
+ const rawLabel = entry !== void 0 && item.items.label !== void 0 ? entry[item.items.label] : void 0;
163128
+ const label = typeof rawLabel === "string" && rawLabel.length > 0 ? rawLabel : scope;
163129
+ if (fixed !== null || item.spec.unitFrom === void 0) return {
163130
+ label,
163131
+ unit: fixed
163132
+ };
163133
+ return {
163134
+ label,
163135
+ unit: entry === void 0 ? null : readUnit(entry, item.spec.unitFrom)
163136
+ };
163137
+ }
163138
+ sliceOf(deviceId, capName, slices) {
163139
+ const pending = slices.get(capName) ?? this.deps.readCapSlice(deviceId, capName).catch(() => null);
163140
+ slices.set(capName, pending);
163141
+ return pending;
163142
+ }
163020
163143
  /** The metric's fixed unit, else the device's own (a `unitFrom` field), else unknown. */
163021
163144
  async unitOf(subject, series, fixed, slices) {
163022
163145
  if (fixed !== null || subject.kind !== "device" || !series.key.startsWith("cap:")) return fixed;
163023
163146
  const spec = this.specOf(series.key);
163024
163147
  if (spec?.unitFrom === void 0) return null;
163025
163148
  const capName = series.key.slice(CAP_KEY_PREFIX.length, series.key.indexOf("."));
163026
- const pending = slices.get(capName) ?? this.deps.readCapSlice(subject.deviceId, capName).catch(() => null);
163027
- slices.set(capName, pending);
163028
- const slice = await pending;
163149
+ const slice = await this.sliceOf(subject.deviceId, capName, slices);
163029
163150
  return slice === null ? null : readUnit(slice, spec.unitFrom);
163030
163151
  }
163031
163152
  specOf(key) {
@@ -163035,10 +163156,149 @@ var require_device_history_addon = __commonJS({
163035
163156
  return this.deps.catalog[body.slice(0, dot)]?.[body.slice(dot + 1)];
163036
163157
  }
163037
163158
  };
163159
+ function isObject(value) {
163160
+ return typeof value === "object" && value !== null && !Array.isArray(value);
163161
+ }
163162
+ function itemFieldsByKey(itemCatalog) {
163163
+ return new Map(Object.entries(itemCatalog).flatMap(([capName, items]) => Object.entries(items.fields).map(([field, spec]) => [capItemSeriesKey(capName, items, field), {
163164
+ capName,
163165
+ items,
163166
+ spec
163167
+ }])));
163168
+ }
163038
163169
  function withoutRaw(tiers) {
163039
163170
  const rollups = tiers.filter((t) => t !== "raw");
163040
163171
  return rollups.length > 0 ? rollups : ["5m"];
163041
163172
  }
163173
+ function judgeOrphan(series, declaredCapKeys, missingSince) {
163174
+ if (series.deviceId === null) return null;
163175
+ if (missingSince !== void 0) return {
163176
+ reason: "device-gone",
163177
+ missingSince
163178
+ };
163179
+ if (series.key.startsWith("cap:") && !declaredCapKeys.has(series.key)) return {
163180
+ reason: "undeclared",
163181
+ missingSince: null
163182
+ };
163183
+ return null;
163184
+ }
163185
+ var OrphanSeries = class {
163186
+ deps;
163187
+ constructor(deps) {
163188
+ this.deps = deps;
163189
+ }
163190
+ verdictOf(series) {
163191
+ const { departures, declaredCapKeys } = this.deps;
163192
+ return judgeOrphan(series, declaredCapKeys, series.deviceId === null ? void 0 : departures.missingSince(series.deviceId));
163193
+ }
163194
+ /** Every orphan, ordered by device, key and scope, the first {@link DEVICE_HISTORY_ORPHAN_LIST_MAX} counted. */
163195
+ async list() {
163196
+ const found = this.deps.registry.all().flatMap((series) => {
163197
+ const verdict = this.verdictOf(series);
163198
+ return verdict === null || series.deviceId === null ? [] : [{
163199
+ series,
163200
+ deviceId: series.deviceId,
163201
+ verdict
163202
+ }];
163203
+ }).toSorted((a, b) => a.deviceId - b.deviceId || a.series.key.localeCompare(b.series.key) || a.series.scope.localeCompare(b.series.scope));
163204
+ const out = [];
163205
+ for (const { series, deviceId, verdict } of found.slice(0, 200)) out.push({
163206
+ seriesId: series.id,
163207
+ deviceId,
163208
+ key: series.key,
163209
+ scope: series.scope,
163210
+ displayName: this.deps.registry.metric(series.key)?.displayName ?? null,
163211
+ reason: verdict.reason,
163212
+ missingSince: verdict.missingSince,
163213
+ firstTs: series.firstTs,
163214
+ lastTs: series.lastTs,
163215
+ rows: await this.rowsOf(series.id)
163216
+ });
163217
+ return {
163218
+ series: out,
163219
+ truncated: found.length > 200
163220
+ };
163221
+ }
163222
+ async rowsOf(seriesId) {
163223
+ const { store } = this.deps;
163224
+ return {
163225
+ raw: await store.countSeriesRows("raw", seriesId),
163226
+ "5m": await store.countSeriesRows("5m", seriesId),
163227
+ "1h": await store.countSeriesRows("1h", seriesId),
163228
+ "1d": await store.countSeriesRows("1d", seriesId)
163229
+ };
163230
+ }
163231
+ /** Delete orphans by id, one at a time, each re-judged now. One entry per id, in order. */
163232
+ async delete(seriesIds) {
163233
+ const out = [];
163234
+ for (const seriesId of seriesIds) out.push(await this.deleteOne(seriesId));
163235
+ return out;
163236
+ }
163237
+ async deleteOne(seriesId) {
163238
+ const { registry, retention, logger, clock } = this.deps;
163239
+ const series = registry.all().find((s) => s.id === seriesId);
163240
+ if (series === void 0) return {
163241
+ seriesId,
163242
+ ok: false,
163243
+ reason: "not-found"
163244
+ };
163245
+ const verdict = this.verdictOf(series);
163246
+ if (verdict === null || series.deviceId === null) {
163247
+ logger.info("device-history: refused to delete a series that is not orphan", {
163248
+ ...series.deviceId === null ? {} : { tags: { deviceId: series.deviceId } },
163249
+ meta: {
163250
+ seriesId,
163251
+ key: series.key,
163252
+ scope: series.scope
163253
+ }
163254
+ });
163255
+ return {
163256
+ seriesId,
163257
+ ok: false,
163258
+ reason: "not-orphan"
163259
+ };
163260
+ }
163261
+ try {
163262
+ const deleted = await retention.purgeSeries(series, clock());
163263
+ registry.remove(series.id);
163264
+ const rows = {
163265
+ raw: deleted.raw,
163266
+ "5m": deleted["5m"],
163267
+ "1h": deleted["1h"],
163268
+ "1d": deleted["1d"]
163269
+ };
163270
+ logger.info("device-history: deleted an orphan series", {
163271
+ tags: { deviceId: series.deviceId },
163272
+ meta: {
163273
+ seriesId,
163274
+ key: series.key,
163275
+ scope: series.scope,
163276
+ reason: verdict.reason,
163277
+ rows
163278
+ }
163279
+ });
163280
+ return {
163281
+ seriesId,
163282
+ ok: true,
163283
+ deleted: rows
163284
+ };
163285
+ } catch (err) {
163286
+ logger.warn("device-history: deleting an orphan series failed \u2014 it stays listed, retry the delete", {
163287
+ tags: { deviceId: series.deviceId },
163288
+ meta: {
163289
+ seriesId,
163290
+ key: series.key,
163291
+ error: err instanceof Error ? err.message : String(err)
163292
+ }
163293
+ });
163294
+ return {
163295
+ seriesId,
163296
+ ok: false,
163297
+ reason: "failed"
163298
+ };
163299
+ }
163300
+ }
163301
+ };
163042
163302
  var HISTORY_NAMESPACE = "device-history";
163043
163303
  var COLLECTIONS = {
163044
163304
  metrics: "metrics",
@@ -163638,6 +163898,17 @@ var require_device_history_addon = __commonJS({
163638
163898
  key: String(seriesId)
163639
163899
  });
163640
163900
  }
163901
+ /**
163902
+ * One series' rows of a tier: a range of the primary key (`series_id = ?`),
163903
+ * never a scan of the tier (D447, asserted in `history-query-plans.spec.ts`).
163904
+ */
163905
+ async countSeriesRows(tier, seriesId) {
163906
+ return this.door.count.query({
163907
+ namespace: HISTORY_NAMESPACE,
163908
+ collection: collectionOf(tier),
163909
+ filter: { where: { series_id: seriesId } }
163910
+ });
163911
+ }
163641
163912
  async countRows(tier) {
163642
163913
  return this.door.count.query({
163643
163914
  namespace: HISTORY_NAMESPACE,
@@ -164157,6 +164428,7 @@ var require_device_history_addon = __commonJS({
164157
164428
  query;
164158
164429
  retentionJob;
164159
164430
  departures;
164431
+ orphans;
164160
164432
  optedOutDevices = /* @__PURE__ */ new Set();
164161
164433
  tierCounts = /* @__PURE__ */ new Map();
164162
164434
  startedAt;
@@ -164165,7 +164437,7 @@ var require_device_history_addon = __commonJS({
164165
164437
  ready = false;
164166
164438
  constructor(deps) {
164167
164439
  this.deps = deps;
164168
- const { logger, clock, catalog } = deps;
164440
+ const { logger, clock, catalog, itemCatalog } = deps;
164169
164441
  this.startedAt = clock();
164170
164442
  this.store = new HistoryStore(deps.door);
164171
164443
  this.registry = new SeriesRegistry(this.store);
@@ -164189,6 +164461,7 @@ var require_device_history_addon = __commonJS({
164189
164461
  const optedOut = () => this.optedOutDevices;
164190
164462
  this.capture = new CaptureEngine({
164191
164463
  catalog,
164464
+ itemCatalog,
164192
164465
  registry: this.registry,
164193
164466
  store: this.store,
164194
164467
  directory: this.directory,
@@ -164222,6 +164495,7 @@ var require_device_history_addon = __commonJS({
164222
164495
  clock,
164223
164496
  retention: deps.retention,
164224
164497
  catalog,
164498
+ itemCatalog,
164225
164499
  readCapSlice: deps.readCapSlice
164226
164500
  });
164227
164501
  this.departures = new DepartureTracker(this.store, this.registry, logger);
@@ -164238,11 +164512,20 @@ var require_device_history_addon = __commonJS({
164238
164512
  counters: this.counters,
164239
164513
  logger
164240
164514
  });
164515
+ this.orphans = new OrphanSeries({
164516
+ store: this.store,
164517
+ registry: this.registry,
164518
+ departures: this.departures,
164519
+ retention: this.retentionJob,
164520
+ declaredCapKeys: new Set(catalogMetrics(catalog, itemCatalog, 0).map((m) => m.key)),
164521
+ logger,
164522
+ clock
164523
+ });
164241
164524
  }
164242
164525
  async prepare() {
164243
164526
  await this.store.declare();
164244
164527
  await this.registry.load();
164245
- for (const metric of catalogMetrics(this.deps.catalog, this.deps.clock())) await this.registry.declareMetric(metric);
164528
+ for (const metric of catalogMetrics(this.deps.catalog, this.deps.itemCatalog, this.deps.clock())) await this.registry.declareMetric(metric);
164246
164529
  this.optedOutDevices = await this.store.loadOptOuts();
164247
164530
  await this.departures.load();
164248
164531
  await this.directory.refresh();
@@ -164361,6 +164644,14 @@ var require_device_history_addon = __commonJS({
164361
164644
  this.optedOutDevices = new Set([...this.optedOutDevices].filter((id) => id !== deviceId));
164362
164645
  return { series };
164363
164646
  }
164647
+ /** Device series no longer linked to a device — reported, never purged here. */
164648
+ listOrphans() {
164649
+ return this.orphans.list();
164650
+ }
164651
+ /** Delete orphan series by id; a series that is not orphan NOW is refused. */
164652
+ deleteOrphans(seriesIds) {
164653
+ return this.orphans.delete(seriesIds);
164654
+ }
164364
164655
  /** Series held per device, for the admin clean-up listing. */
164365
164656
  seriesByDevice() {
164366
164657
  const out = /* @__PURE__ */ new Map();
@@ -164532,7 +164823,9 @@ var require_device_history_addon = __commonJS({
164532
164823
  recordBuckets: async (input) => this.requireRuntime().ingest.recordBuckets(callerOf(input), input.buckets),
164533
164824
  getDeviceEnabled: async (input) => ({ enabled: this.requireRuntime().isEnabled(input.deviceId) }),
164534
164825
  setDeviceEnabled: async (input) => ({ enabled: await this.requireRuntime().setEnabled(input.deviceId, input.enabled) }),
164535
- getStatus: async () => this.requireRuntime().status()
164826
+ getStatus: async () => this.requireRuntime().status(),
164827
+ listOrphanSeries: async () => this.requireRuntime().listOrphans(),
164828
+ deleteOrphanSeries: async (input) => ({ results: await this.requireRuntime().deleteOrphans(input.seriesIds) })
164536
164829
  };
164537
164830
  this.emitStoreReadiness("starting");
164538
164831
  this.ctx.logger.info("Initialized \u2014 device-history provider registered (store opens on hub link)");
@@ -164568,6 +164861,7 @@ var require_device_history_addon = __commonJS({
164568
164861
  const runtime = new HistoryRuntime({
164569
164862
  door: api.settingsStore,
164570
164863
  catalog: require_dist10.HISTORY_CATALOG,
164864
+ itemCatalog: require_dist10.HISTORY_ITEM_CATALOG,
164571
164865
  deviceSource: {
164572
164866
  listAll: async () => {
164573
164867
  return (await api.deviceManager.listAll.query({ projection: "slim" })).map((d) => ({
@@ -164685,7 +164979,7 @@ var require_filesystem_storage_addon = __commonJS({
164685
164979
  [Symbol.toStringTag]: { value: "Module" }
164686
164980
  });
164687
164981
  var require_chunk = require_chunk_Cek0wNdY();
164688
- var require_dist10 = require_dist_QyW9hVO2();
164982
+ var require_dist10 = require_dist_DuBtzSCh();
164689
164983
  var node_crypto = __require("crypto");
164690
164984
  var node_fs_promises = __require("fs/promises");
164691
164985
  var node_path = __require("path");
@@ -165795,12 +166089,12 @@ var require_lib = __commonJS({
165795
166089
  }
165796
166090
  });
165797
166091
 
165798
- // ../system/dist/wal-maintenance-BIpPCYDi.js
165799
- var require_wal_maintenance_BIpPCYDi = __commonJS({
165800
- "../system/dist/wal-maintenance-BIpPCYDi.js"(exports) {
166092
+ // ../system/dist/wal-maintenance-EK28XXnc.js
166093
+ var require_wal_maintenance_EK28XXnc = __commonJS({
166094
+ "../system/dist/wal-maintenance-EK28XXnc.js"(exports) {
165801
166095
  "use strict";
165802
166096
  var require_chunk = require_chunk_Cek0wNdY();
165803
- var require_dist10 = require_dist_QyW9hVO2();
166097
+ var require_dist10 = require_dist_DuBtzSCh();
165804
166098
  var require_prefix_range = require_prefix_range_D_KueXbQ();
165805
166099
  var node_crypto = __require("crypto");
165806
166100
  var node_module = __require("module");
@@ -169984,9 +170278,9 @@ var require_sqlite_settings_addon = __commonJS({
169984
170278
  [Symbol.toStringTag]: { value: "Module" }
169985
170279
  });
169986
170280
  var require_chunk = require_chunk_Cek0wNdY();
169987
- var require_dist10 = require_dist_QyW9hVO2();
170281
+ var require_dist10 = require_dist_DuBtzSCh();
169988
170282
  var require_retired_settings_keys = require_retired_settings_keys_CGUqX_Oz();
169989
- var require_wal_maintenance = require_wal_maintenance_BIpPCYDi();
170283
+ var require_wal_maintenance = require_wal_maintenance_EK28XXnc();
169990
170284
  var require_sqlite_snapshot_protocol = require_sqlite_snapshot_protocol_B9OndqZv();
169991
170285
  var require_wal_checkpoint_policy = require_wal_checkpoint_policy_d2z2qPqx();
169992
170286
  var node_fs_promises = __require("fs/promises");
@@ -171614,7 +171908,7 @@ var require_sqlite_storage = __commonJS({
171614
171908
  });
171615
171909
  require_chunk_Cek0wNdY();
171616
171910
  var require_builtins_sqlite_storage_filesystem_storage_addon = require_filesystem_storage_addon();
171617
- var require_wal_maintenance = require_wal_maintenance_BIpPCYDi();
171911
+ var require_wal_maintenance = require_wal_maintenance_EK28XXnc();
171618
171912
  var require_builtins_sqlite_storage_sqlite_settings_addon = require_sqlite_settings_addon();
171619
171913
  var ConfigStore = class {
171620
171914
  db;
@@ -171698,7 +171992,7 @@ var require_storage_orchestrator_addon = __commonJS({
171698
171992
  [Symbol.toStringTag]: { value: "Module" }
171699
171993
  });
171700
171994
  var require_chunk = require_chunk_Cek0wNdY();
171701
- var require_dist10 = require_dist_QyW9hVO2();
171995
+ var require_dist10 = require_dist_DuBtzSCh();
171702
171996
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
171703
171997
  var zod = require_zod();
171704
171998
  var node_crypto = __require("crypto");
@@ -176285,12 +176579,12 @@ var require_storage_orchestrator = __commonJS({
176285
176579
  }
176286
176580
  });
176287
176581
 
176288
- // ../system/dist/agent-https-OIvfjiTx.js
176289
- var require_agent_https_OIvfjiTx = __commonJS({
176290
- "../system/dist/agent-https-OIvfjiTx.js"(exports) {
176582
+ // ../system/dist/agent-https-5OgOR-eF.js
176583
+ var require_agent_https_5OgOR_eF = __commonJS({
176584
+ "../system/dist/agent-https-5OgOR-eF.js"(exports) {
176291
176585
  "use strict";
176292
176586
  var require_chunk = require_chunk_Cek0wNdY();
176293
- var require_dist10 = require_dist_QyW9hVO2();
176587
+ var require_dist10 = require_dist_DuBtzSCh();
176294
176588
  var require_x509_es = require_x509_es_C4cOo48M();
176295
176589
  var zod = require_zod();
176296
176590
  var node_crypto = __require("crypto");
@@ -177348,11 +177642,11 @@ var require_agent_https_OIvfjiTx = __commonJS({
177348
177642
  }
177349
177643
  });
177350
177644
 
177351
- // ../system/dist/constants-CdgJF4V6.js
177352
- var require_constants_CdgJF4V6 = __commonJS({
177353
- "../system/dist/constants-CdgJF4V6.js"(exports) {
177645
+ // ../system/dist/constants-2mPqJjkR.js
177646
+ var require_constants_2mPqJjkR = __commonJS({
177647
+ "../system/dist/constants-2mPqJjkR.js"(exports) {
177354
177648
  "use strict";
177355
- var MESH_CA_KEY_REF = require_dist_QyW9hVO2().buildSecretRef({
177649
+ var MESH_CA_KEY_REF = require_dist_DuBtzSCh().buildSecretRef({
177356
177650
  owner: "system",
177357
177651
  scope: { kind: "global" },
177358
177652
  fieldPath: "meshCaKey"
@@ -194449,11 +194743,11 @@ var require_system_config_addon = __commonJS({
194449
194743
  [Symbol.toStringTag]: { value: "Module" }
194450
194744
  });
194451
194745
  var require_chunk = require_chunk_Cek0wNdY();
194452
- var require_dist10 = require_dist_QyW9hVO2();
194746
+ var require_dist10 = require_dist_DuBtzSCh();
194453
194747
  var require_safe_buffer$1 = require_safe_buffer_D3aoa_7X();
194454
194748
  var require_x509_es = require_x509_es_C4cOo48M();
194455
- var require_agent_https = require_agent_https_OIvfjiTx();
194456
- var require_constants$8 = require_constants_CdgJF4V6();
194749
+ var require_agent_https = require_agent_https_5OgOR_eF();
194750
+ var require_constants$8 = require_constants_2mPqJjkR();
194457
194751
  var require_src$1 = require_src_CnAqG6EF();
194458
194752
  var zod = require_zod();
194459
194753
  var node_crypto = __require("crypto");
@@ -275505,7 +275799,7 @@ var require_winston_logging = __commonJS({
275505
275799
  [Symbol.toStringTag]: { value: "Module" }
275506
275800
  });
275507
275801
  var require_chunk = require_chunk_Cek0wNdY();
275508
- var require_dist10 = require_dist_QyW9hVO2();
275802
+ var require_dist10 = require_dist_DuBtzSCh();
275509
275803
  var require_formatter = require_formatter_DqAKDlvN();
275510
275804
  var require_log_file_read_protocol = require_log_file_read_protocol_D0WcLbIV();
275511
275805
  var node_path = __require("path");
@@ -283308,14 +283602,14 @@ var require_lib_8oZrELLm = __commonJS({
283308
283602
  }
283309
283603
  });
283310
283604
 
283311
- // ../system/dist/hub-mdns-ciao-CWkX17zX.js
283312
- var require_hub_mdns_ciao_CWkX17zX = __commonJS({
283313
- "../system/dist/hub-mdns-ciao-CWkX17zX.js"(exports) {
283605
+ // ../system/dist/hub-mdns-ciao-BLsX_2cL.js
283606
+ var require_hub_mdns_ciao_BLsX_2cL = __commonJS({
283607
+ "../system/dist/hub-mdns-ciao-BLsX_2cL.js"(exports) {
283314
283608
  "use strict";
283315
283609
  var require_chunk = require_chunk_Cek0wNdY();
283316
- var require_dist10 = require_dist_QyW9hVO2();
283610
+ var require_dist10 = require_dist_DuBtzSCh();
283317
283611
  var require_x509_es = require_x509_es_C4cOo48M();
283318
- var require_agent_https = require_agent_https_OIvfjiTx();
283612
+ var require_agent_https = require_agent_https_5OgOR_eF();
283319
283613
  var node_crypto = __require("crypto");
283320
283614
  var _camstack_types_node = require_node();
283321
283615
  var node_fs_promises = __require("fs/promises");
@@ -284327,12 +284621,12 @@ var require_platform_key_source_Di7HrlGR = __commonJS({
284327
284621
  }
284328
284622
  });
284329
284623
 
284330
- // ../system/dist/restore-archive-DMgpNgX5.js
284331
- var require_restore_archive_DMgpNgX5 = __commonJS({
284332
- "../system/dist/restore-archive-DMgpNgX5.js"(exports) {
284624
+ // ../system/dist/restore-archive-CxPAuaNs.js
284625
+ var require_restore_archive_CxPAuaNs = __commonJS({
284626
+ "../system/dist/restore-archive-CxPAuaNs.js"(exports) {
284333
284627
  "use strict";
284334
284628
  var require_chunk = require_chunk_Cek0wNdY();
284335
- var require_dist10 = require_dist_QyW9hVO2();
284629
+ var require_dist10 = require_dist_DuBtzSCh();
284336
284630
  var zod = require_zod();
284337
284631
  var node_crypto = __require("crypto");
284338
284632
  var buffer = __require("buffer");
@@ -295573,8 +295867,8 @@ var require_dist3 = __commonJS({
295573
295867
  "use strict";
295574
295868
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
295575
295869
  var require_chunk = require_chunk_Cek0wNdY();
295576
- var require_dist10 = require_dist_QyW9hVO2();
295577
- var require_device_ca = require_device_ca_DpvQ5wrb();
295870
+ var require_dist10 = require_dist_DuBtzSCh();
295871
+ var require_device_ca = require_device_ca_CC58lmNV();
295578
295872
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
295579
295873
  var require_builtins_alerts_alerts_addon = require_alerts_addon();
295580
295874
  require_alerts();
@@ -295599,21 +295893,21 @@ var require_dist3 = __commonJS({
295599
295893
  var require_builtins_native_metrics_native_metrics_addon = require_native_metrics_addon();
295600
295894
  var require_builtins_device_history_device_history_addon = require_device_history_addon();
295601
295895
  var require_builtins_sqlite_storage_filesystem_storage_addon = require_filesystem_storage_addon();
295602
- var require_wal_maintenance = require_wal_maintenance_BIpPCYDi();
295896
+ var require_wal_maintenance = require_wal_maintenance_EK28XXnc();
295603
295897
  var require_builtins_sqlite_storage_sqlite_settings_addon = require_sqlite_settings_addon();
295604
295898
  var require_builtins_sqlite_storage_index = require_sqlite_storage();
295605
295899
  var require_builtins_storage_orchestrator_storage_orchestrator_addon = require_storage_orchestrator_addon();
295606
295900
  require_storage_orchestrator();
295607
- var require_agent_https = require_agent_https_OIvfjiTx();
295608
- var require_constants15 = require_constants_CdgJF4V6();
295901
+ var require_agent_https = require_agent_https_5OgOR_eF();
295902
+ var require_constants15 = require_constants_2mPqJjkR();
295609
295903
  var require_builtins_system_config_system_config_addon = require_system_config_addon();
295610
295904
  require_system_config();
295611
295905
  var require_builtins_winston_logging_index = require_winston_logging();
295612
295906
  var require_custom_action_registry = require_custom_action_registry_MRyes5j0();
295613
- var require_hub_mdns_ciao = require_hub_mdns_ciao_CWkX17zX();
295907
+ var require_hub_mdns_ciao = require_hub_mdns_ciao_BLsX_2cL();
295614
295908
  var require_platform_key_source = require_platform_key_source_Di7HrlGR();
295615
295909
  var require_resource_monitor = require_resource_monitor_mio9dSCT();
295616
- var require_restore_archive = require_restore_archive_DMgpNgX5();
295910
+ var require_restore_archive = require_restore_archive_CxPAuaNs();
295617
295911
  var require_coral_accelerators = require_coral_accelerators_oUXmmbc2();
295618
295912
  var zod = require_zod();
295619
295913
  var node_crypto = __require("crypto");
@@ -319057,7 +319351,7 @@ var require_dist4 = __commonJS({
319057
319351
  "../types/dist/index.js"(exports) {
319058
319352
  "use strict";
319059
319353
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
319060
- var require_sleep = require_sleep_CD18m9HF();
319354
+ var require_sleep = require_sleep_DVovpDRU();
319061
319355
  var require_event_category = require_event_category_7AwNbPjm();
319062
319356
  var require_composition = require_composition_eoYYVfOp();
319063
319357
  var require_node_color2 = require_node_color();
@@ -324559,7 +324853,11 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
324559
324853
  zod.z.literal(4)
324560
324854
  ]).optional(),
324561
324855
  tab: zod.z.string().optional(),
324562
- location: zod.z.enum(["settings", "top-tab"]).optional(),
324856
+ location: zod.z.enum([
324857
+ "settings",
324858
+ "top-tab",
324859
+ "hero"
324860
+ ]).optional(),
324563
324861
  order: zod.z.number().optional(),
324564
324862
  fields: zod.z.array(zod.z.any())
324565
324863
  });
@@ -329905,6 +330203,48 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
329905
330203
  approxBytes: zod.z.number().nullable(),
329906
330204
  countedAt: zod.z.number().nullable()
329907
330205
  });
330206
+ var DeviceHistoryOrphanReasonSchema = zod.z.enum(["device-gone", "undeclared"]);
330207
+ var TierRowsSchema = zod.z.object({
330208
+ raw: zod.z.number().int(),
330209
+ "5m": zod.z.number().int(),
330210
+ "1h": zod.z.number().int(),
330211
+ "1d": zod.z.number().int()
330212
+ });
330213
+ var OrphanSeriesSchema = zod.z.object({
330214
+ seriesId: zod.z.number().int(),
330215
+ deviceId: zod.z.number().int(),
330216
+ key: zod.z.string(),
330217
+ scope: zod.z.string(),
330218
+ /** The metric's display name; `null` when its metric row is gone too. */
330219
+ displayName: zod.z.string().nullable(),
330220
+ reason: DeviceHistoryOrphanReasonSchema,
330221
+ /** `device-gone`: since when the device has been absent; `null` otherwise. */
330222
+ missingSince: zod.z.number().nullable(),
330223
+ firstTs: zod.z.number(),
330224
+ lastTs: zod.z.number(),
330225
+ rows: TierRowsSchema
330226
+ });
330227
+ var DEVICE_HISTORY_ORPHAN_LIST_MAX = 200;
330228
+ var DEVICE_HISTORY_ORPHAN_DELETE_MAX = 50;
330229
+ var DeviceHistoryOrphanDeleteRefusalSchema = zod.z.enum([
330230
+ "not-found",
330231
+ "not-orphan",
330232
+ "failed"
330233
+ ]);
330234
+ var OrphanDeleteEntrySchema = zod.z.discriminatedUnion("ok", [zod.z.object({
330235
+ seriesId: zod.z.number().int(),
330236
+ ok: zod.z.literal(true),
330237
+ deleted: TierRowsSchema
330238
+ }), zod.z.object({
330239
+ seriesId: zod.z.number().int(),
330240
+ ok: zod.z.literal(false),
330241
+ reason: DeviceHistoryOrphanDeleteRefusalSchema
330242
+ })]);
330243
+ var OrphanListSchema = zod.z.object({
330244
+ series: zod.z.array(OrphanSeriesSchema).readonly(),
330245
+ /** More orphans exist than {@link DEVICE_HISTORY_ORPHAN_LIST_MAX}. */
330246
+ truncated: zod.z.boolean()
330247
+ });
329908
330248
  var DeviceHistoryStatusSchema = zod.z.object({
329909
330249
  startedAt: zod.z.number(),
329910
330250
  /** The store is declared, series loaded and the first reconcile has answered. */
@@ -330046,7 +330386,24 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
330046
330386
  kind: "mutation",
330047
330387
  auth: "admin"
330048
330388
  }),
330049
- getStatus: require_sleep.method(zod.z.object({}), DeviceHistoryStatusSchema, { auth: "admin" })
330389
+ getStatus: require_sleep.method(zod.z.object({}), DeviceHistoryStatusSchema, { auth: "admin" }),
330390
+ /**
330391
+ * Device series no longer linked to a device (D917): the device is absent
330392
+ * from the listings, or the key is declared by no capability. REPORTED,
330393
+ * never purged by this judgement; each entry counts its rows per tier
330394
+ * (a primary-key range per series). Ordered by device, key, scope.
330395
+ */
330396
+ listOrphanSeries: require_sleep.method(zod.z.object({}), OrphanListSchema, { auth: "admin" }),
330397
+ /**
330398
+ * Delete orphan series by id. Each id is re-judged NOW: a series that is
330399
+ * not orphan (its device came back) is refused `not-orphan`, an unknown id
330400
+ * `not-found`. Deletes tier by tier in the retention slices, then the
330401
+ * series row; one entry per id, in order.
330402
+ */
330403
+ deleteOrphanSeries: require_sleep.method(zod.z.object({ seriesIds: zod.z.array(zod.z.number().int()).min(1).max(50) }), zod.z.object({ results: zod.z.array(OrphanDeleteEntrySchema).readonly() }), {
330404
+ kind: "mutation",
330405
+ auth: "admin"
330406
+ })
330050
330407
  }
330051
330408
  };
330052
330409
  var DeviceCaStatusSchema = zod.z.discriminatedUnion("state", [
@@ -362617,7 +362974,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
362617
362974
  modelId: zod.z.string()
362618
362975
  }),
362619
362976
  zod.z.object({ kind: zod.z.literal("cpu-device") }),
362620
- zod.z.object({ kind: zod.z.literal("coral-untouched") }),
362621
362977
  zod.z.object({ kind: zod.z.literal("device-disabled") }),
362622
362978
  zod.z.object({ kind: zod.z.literal("device-unavailable") }),
362623
362979
  zod.z.object({ kind: zod.z.literal("node-offline") }),
@@ -362710,11 +363066,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
362710
363066
  kind: zod.z.literal("detector-store-unreadable"),
362711
363067
  error: zod.z.string()
362712
363068
  }),
362713
- zod.z.object({
362714
- kind: zod.z.literal("coral-enabled"),
362715
- nodeId: zod.z.string(),
362716
- deviceKey: zod.z.string()
362717
- }),
362718
363069
  zod.z.object({
362719
363070
  kind: zod.z.literal("custom-pin-kept"),
362720
363071
  nodeId: zod.z.string(),
@@ -370402,6 +370753,41 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
370402
370753
  * whether persisting is worth a SQLite commit. */
370403
370754
  volatileStateFields: ["lastChangedAt"]
370404
370755
  };
370756
+ var ConsumableKindSchema = zod.z.enum([
370757
+ "wear",
370758
+ "supply",
370759
+ "waste"
370760
+ ]);
370761
+ var ConsumableFormSchema = zod.z.enum([
370762
+ "part",
370763
+ "tank",
370764
+ "product",
370765
+ "bin"
370766
+ ]);
370767
+ var ConsumableStatusSchema = zod.z.enum([
370768
+ "ok",
370769
+ "low",
370770
+ "empty",
370771
+ "full",
370772
+ "replace"
370773
+ ]);
370774
+ var ConsumableLevelSchema = zod.z.enum([
370775
+ "ok",
370776
+ "low",
370777
+ "empty",
370778
+ "full"
370779
+ ]);
370780
+ function consumableKindOf(item) {
370781
+ return item.kind ?? "wear";
370782
+ }
370783
+ var FORM_OF_KIND = {
370784
+ wear: "part",
370785
+ waste: "bin",
370786
+ supply: "product"
370787
+ };
370788
+ function consumableFormOf(item) {
370789
+ return item.form ?? FORM_OF_KIND[consumableKindOf(item)];
370790
+ }
370405
370791
  var ConsumableRemainingUnitSchema = zod.z.enum([
370406
370792
  "days",
370407
370793
  "hours",
@@ -370416,10 +370802,14 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
370416
370802
  key: zod.z.string().min(1),
370417
370803
  /** Display name. */
370418
370804
  label: zod.z.string().min(1),
370419
- /** Remaining life % when known (0..100). */
370805
+ /** Remaining life % (`wear`) or fill % (`supply`, `waste`) when known (0..100). */
370420
370806
  level: zod.z.number().min(0).max(100).nullable(),
370421
- /** Discrete state when known (binary mode). */
370422
- status: zod.z.enum(["ok", "replace"]).nullable(),
370807
+ /** The provider's discrete verdict when known (binary mode). */
370808
+ status: ConsumableStatusSchema.nullable(),
370809
+ /** What the item is — decides whether low or full is bad. Absent = `wear`. */
370810
+ kind: ConsumableKindSchema.optional(),
370811
+ /** How the item is drawn (part, tank, product, bin). Absent = derived from `kind`. */
370812
+ form: ConsumableFormSchema.optional(),
370423
370813
  /** Ms epoch of the last replace, when known. */
370424
370814
  lastResetAt: zod.z.number().nullable(),
370425
370815
  /** Whether `reset()` is meaningful for this item. */
@@ -370442,7 +370832,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
370442
370832
  kind: "widget",
370443
370833
  widgetId: "host/consumables-panel",
370444
370834
  tab: "consumables",
370445
- topTab: true,
370835
+ placement: "hero",
370446
370836
  label: "Consumables",
370447
370837
  order: 5
370448
370838
  } },
@@ -370504,9 +370894,37 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
370504
370894
  */
370505
370895
  durability: "restored",
370506
370896
  /**
370507
- * History (D850): none — `items` is an array keyed by runtime ids; a history path never enters it.
370897
+ * History (D850, D917): one series per (item field, item `key`) — the item key
370898
+ * is the series scope, the item `label` names it. A vacuum tank's fill, a
370899
+ * brush's remaining life and a dishwasher's salt are each recorded the moment
370900
+ * their provider reports them; an item that appears later gets its series.
370508
370901
  */
370509
- history: "none",
370902
+ history: {
370903
+ fields: {},
370904
+ items: {
370905
+ path: "items",
370906
+ key: "key",
370907
+ label: "label",
370908
+ fields: {
370909
+ level: {
370910
+ kind: "gauge",
370911
+ unit: "%",
370912
+ deadband: 2,
370913
+ displayName: "Level"
370914
+ },
370915
+ status: {
370916
+ kind: "enum",
370917
+ displayName: "Status"
370918
+ },
370919
+ "remaining.value": {
370920
+ kind: "gauge",
370921
+ unitFrom: "remaining.unit",
370922
+ deadband: 1,
370923
+ displayName: "Remaining"
370924
+ }
370925
+ }
370926
+ }
370927
+ },
370510
370928
  /** Clocks, not content: a re-read that changed no item costs no commit. */
370511
370929
  volatileStateFields: ["lastChangedAt", "lastFetchedAt"]
370512
370930
  };
@@ -370867,11 +371285,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
370867
371285
  "offline",
370868
371286
  "description-missing"
370869
371287
  ]);
370870
- var DishwasherConsumableLevelSchema = zod.z.enum([
370871
- "ok",
370872
- "low",
370873
- "empty"
370874
- ]);
370875
371288
  var DishwasherStatusSchema = zod.z.object({
370876
371289
  availability: DishwasherAvailabilitySchema,
370877
371290
  /** Coarse state for compact surfaces; `unavailable` whenever the appliance state is not known. */
@@ -370904,8 +371317,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
370904
371317
  powerOn: zod.z.boolean().nullable(),
370905
371318
  /** The raw power state (`On`, `Off`, `Standby`, …); null when not known. */
370906
371319
  powerState: zod.z.string().nullable(),
370907
- saltLevel: DishwasherConsumableLevelSchema.nullable(),
370908
- rinseAidLevel: DishwasherConsumableLevelSchema.nullable(),
370909
371320
  /**
370910
371321
  * True while the appliance asks for its filter to be cleaned (smart-filter, filter-system or
370911
371322
  * combined care-and-filter reminder raised); false when every such reminder reads clear; null
@@ -379599,16 +380010,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
379599
380010
  "drying",
379600
380011
  "error"
379601
380012
  ]);
379602
- var TankStatusSchema = zod.z.object({
379603
- /** Numeric fill 0..100 when the hardware reports a percentage; null otherwise. */
379604
- level: zod.z.number().min(0).max(100).nullable(),
379605
- /** Discrete state when the hardware is binary-mode; null otherwise. */
379606
- status: zod.z.enum([
379607
- "ok",
379608
- "low",
379609
- "full"
379610
- ]).nullable()
379611
- });
379612
380013
  var VacuumControlStatusSchema = zod.z.object({
379613
380014
  /** Lifecycle state of the vacuum. */
379614
380015
  state: VacuumStateSchema,
@@ -379620,14 +380021,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
379620
380021
  fanSpeed: zod.z.string().nullable(),
379621
380022
  /** Speed tokens the hardware accepts — drives the UI selector. */
379622
380023
  availableFanSpeeds: zod.z.array(zod.z.string()),
379623
- /** Clean-water (mop) tank. Null when the hardware has no clean-water tank. */
379624
- cleanWater: TankStatusSchema.nullable(),
379625
- /** Dirty-water (recovery) tank. Null when the hardware has no dirty-water tank. */
379626
- dirtyWater: TankStatusSchema.nullable(),
379627
- /** Detergent tank. Null when the hardware has no detergent tank. */
379628
- detergent: TankStatusSchema.nullable(),
379629
- /** Dust bin. Null when the hardware has no dust bin. */
379630
- dustBin: TankStatusSchema.nullable(),
379631
380024
  /** 0..100 cleaning-completion percentage of the current task, or null. */
379632
380025
  progressPercent: zod.z.number().min(0).max(100).nullable(),
379633
380026
  /** Current error code (0 / null = no error). */
@@ -379689,35 +380082,14 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
379689
380082
  */
379690
380083
  durability: "session",
379691
380084
  /**
379692
- * History (D850): recorded series. Tank levels are fixed-object paths (`cleanWater.level`, ...).
380085
+ * History (D850): recorded series. The tanks moved to `consumables` (D915), whose
380086
+ * items are keyed by runtime ids and record no history.
379693
380087
  */
379694
- history: { fields: {
379695
- batteryLevel: {
379696
- kind: "gauge",
379697
- unit: "%",
379698
- deadband: 2
379699
- },
379700
- "cleanWater.level": {
379701
- kind: "gauge",
379702
- unit: "%",
379703
- deadband: 2
379704
- },
379705
- "dirtyWater.level": {
379706
- kind: "gauge",
379707
- unit: "%",
379708
- deadband: 2
379709
- },
379710
- "detergent.level": {
379711
- kind: "gauge",
379712
- unit: "%",
379713
- deadband: 2
379714
- },
379715
- "dustBin.level": {
379716
- kind: "gauge",
379717
- unit: "%",
379718
- deadband: 2
379719
- }
379720
- } }
380088
+ history: { fields: { batteryLevel: {
380089
+ kind: "gauge",
380090
+ unit: "%",
380091
+ deadband: 2
380092
+ } } }
379721
380093
  };
379722
380094
  var ValveStateSchema = zod.z.enum([
379723
380095
  "open",
@@ -385351,6 +385723,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
385351
385723
  addonId: null,
385352
385724
  access: "create"
385353
385725
  },
385726
+ "deviceHistory.deleteOrphanSeries": {
385727
+ capName: "device-history",
385728
+ capScope: "system",
385729
+ addonId: null,
385730
+ access: "delete"
385731
+ },
385354
385732
  "deviceHistory.getDeviceEnabled": {
385355
385733
  capName: "device-history",
385356
385734
  capScope: "system",
@@ -385369,6 +385747,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
385369
385747
  addonId: null,
385370
385748
  access: "view"
385371
385749
  },
385750
+ "deviceHistory.listOrphanSeries": {
385751
+ capName: "device-history",
385752
+ capScope: "system",
385753
+ addonId: null,
385754
+ access: "view"
385755
+ },
385372
385756
  "deviceHistory.listSeries": {
385373
385757
  capName: "device-history",
385374
385758
  capScope: "system",
@@ -398896,33 +399280,11 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
398896
399280
  unit: "\xB0C",
398897
399281
  deadband: 0.1
398898
399282
  } },
398899
- "vacuum-control": {
398900
- "batteryLevel": {
398901
- kind: "gauge",
398902
- unit: "%",
398903
- deadband: 2
398904
- },
398905
- "cleanWater.level": {
398906
- kind: "gauge",
398907
- unit: "%",
398908
- deadband: 2
398909
- },
398910
- "dirtyWater.level": {
398911
- kind: "gauge",
398912
- unit: "%",
398913
- deadband: 2
398914
- },
398915
- "detergent.level": {
398916
- kind: "gauge",
398917
- unit: "%",
398918
- deadband: 2
398919
- },
398920
- "dustBin.level": {
398921
- kind: "gauge",
398922
- unit: "%",
398923
- deadband: 2
398924
- }
398925
- },
399283
+ "vacuum-control": { "batteryLevel": {
399284
+ kind: "gauge",
399285
+ unit: "%",
399286
+ deadband: 2
399287
+ } },
398926
399288
  "valve": { "position": {
398927
399289
  kind: "gauge",
398928
399290
  unit: "%",
@@ -398964,12 +399326,41 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
398964
399326
  }
398965
399327
  }
398966
399328
  };
399329
+ var HISTORY_ITEM_CATALOG = { "consumables": {
399330
+ path: "items",
399331
+ key: "key",
399332
+ label: "label",
399333
+ fields: {
399334
+ "level": {
399335
+ kind: "gauge",
399336
+ unit: "%",
399337
+ deadband: 2,
399338
+ displayName: "Level"
399339
+ },
399340
+ "status": {
399341
+ kind: "enum",
399342
+ displayName: "Status",
399343
+ values: [
399344
+ "ok",
399345
+ "low",
399346
+ "empty",
399347
+ "full",
399348
+ "replace"
399349
+ ]
399350
+ },
399351
+ "remaining.value": {
399352
+ kind: "gauge",
399353
+ unitFrom: "remaining.unit",
399354
+ deadband: 1,
399355
+ displayName: "Remaining"
399356
+ }
399357
+ }
399358
+ } };
398967
399359
  var HISTORY_NONE_CAPS = [
398968
399360
  "audio-metrics",
398969
399361
  "automation-control",
398970
399362
  "camera-streams",
398971
399363
  "color",
398972
- "consumables",
398973
399364
  "control",
398974
399365
  "day-night",
398975
399366
  "device-discovery",
@@ -399254,7 +399645,9 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
399254
399645
  declareSeries: (input) => dispatch("deviceHistory", "declareSeries", "mutation", input),
399255
399646
  record: (input) => dispatch("deviceHistory", "record", "mutation", input),
399256
399647
  recordBuckets: (input) => dispatch("deviceHistory", "recordBuckets", "mutation", input),
399257
- getStatus: (input) => dispatch("deviceHistory", "getStatus", "query", input)
399648
+ getStatus: (input) => dispatch("deviceHistory", "getStatus", "query", input),
399649
+ listOrphanSeries: (input) => dispatch("deviceHistory", "listOrphanSeries", "query", input),
399650
+ deleteOrphanSeries: (input) => dispatch("deviceHistory", "deleteOrphanSeries", "mutation", input)
399258
399651
  },
399259
399652
  deviceManager: {
399260
399653
  allocateDeviceId: (input) => dispatch("deviceManager", "allocateDeviceId", "mutation", input),
@@ -402344,17 +402737,18 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
402344
402737
  {
402345
402738
  name: "item",
402346
402739
  label: "Consumable",
402740
+ description: "The device's own name for the item: a filter, a brush, the salt, a tank.",
402347
402741
  example: "Filtro",
402348
402742
  group: "system",
402349
402743
  families: ["rule"],
402350
402744
  fields: [...RULE_TEXT],
402351
402745
  kinds: ["system"],
402352
- systemEventKinds: [...CONSUMABLE]
402746
+ systemEventKinds: [...CONSUMABLE, ...TANK]
402353
402747
  },
402354
402748
  {
402355
402749
  name: "tank",
402356
402750
  label: "Tank",
402357
- description: "Which tank: clean water, dirty water, detergent or dust bin.",
402751
+ description: "Which tank, as a stable token: cleanWater, dirtyWater, detergent, dustBin, or the device key of any other supply (salt, rinse aid).",
402358
402752
  example: "clean water",
402359
402753
  group: "system",
402360
402754
  families: ["rule"],
@@ -404553,7 +404947,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
404553
404947
  fps: 19,
404554
404948
  measured: true
404555
404949
  }
404556
- }
404950
+ },
404951
+ coral: { "yolov9s-320-edgetpu": {
404952
+ p50Ms: 32,
404953
+ fps: 31,
404954
+ measured: false
404955
+ } }
404557
404956
  },
404558
404957
  "intel-n-series": {
404559
404958
  "intel-igpu": {
@@ -404679,7 +405078,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
404679
405078
  fps: 3,
404680
405079
  measured: true
404681
405080
  }
404682
- }
405081
+ },
405082
+ coral: { "yolov9s-320-edgetpu": {
405083
+ p50Ms: 32,
405084
+ fps: 31,
405085
+ measured: false
405086
+ } }
404683
405087
  },
404684
405088
  "amd-phoenix": {
404685
405089
  "x86-cpu": {
@@ -404746,96 +405150,120 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
404746
405150
  },
404747
405151
  "amd-igpu": {
404748
405152
  "yolov9t-320": {
404749
- p50Ms: 188,
404750
- fps: 5,
405153
+ p50Ms: 40,
405154
+ fps: 25,
404751
405155
  measured: true
404752
405156
  },
404753
405157
  "yolov9t-640": {
404754
- p50Ms: 671,
404755
- fps: 1,
405158
+ p50Ms: 89,
405159
+ fps: 11,
404756
405160
  measured: true
404757
405161
  },
404758
405162
  "yolov9s-320": {
404759
- p50Ms: 401,
404760
- fps: 2,
405163
+ p50Ms: 65,
405164
+ fps: 15,
404761
405165
  measured: true
404762
405166
  },
404763
405167
  "yolov9s-640": {
404764
- p50Ms: 1434,
404765
- fps: 1,
405168
+ p50Ms: 125,
405169
+ fps: 8,
404766
405170
  measured: true
404767
405171
  },
404768
405172
  "yolov9m-320": {
404769
- p50Ms: 1076,
404770
- fps: 1,
405173
+ p50Ms: 85,
405174
+ fps: 12,
405175
+ measured: true
405176
+ },
405177
+ "yolov9m-640": {
405178
+ p50Ms: 228,
405179
+ fps: 4,
404771
405180
  measured: true
404772
405181
  }
404773
- }
404774
- },
404775
- "apple-m-pro": { "apple-ane": {
404776
- "yolov9t-320": {
404777
- p50Ms: 1.1,
404778
- fps: 952,
404779
- measured: true
404780
- },
404781
- "yolov9t-640": {
404782
- p50Ms: 3,
404783
- fps: 333,
404784
- measured: true
404785
405182
  },
404786
- "yolov9s-320": {
404787
- p50Ms: 1.5,
404788
- fps: 667,
404789
- measured: true
404790
- },
404791
- "yolov9s-640": {
404792
- p50Ms: 5.1,
404793
- fps: 196,
404794
- measured: true
404795
- },
404796
- "yolov9m-320": {
404797
- p50Ms: 3.2,
404798
- fps: 312,
405183
+ coral: { "yolov9s-320-edgetpu": {
405184
+ p50Ms: 32,
405185
+ fps: 31,
404799
405186
  measured: true
405187
+ } }
405188
+ },
405189
+ "apple-m-pro": {
405190
+ "apple-ane": {
405191
+ "yolov9t-320": {
405192
+ p50Ms: 1.1,
405193
+ fps: 952,
405194
+ measured: true
405195
+ },
405196
+ "yolov9t-640": {
405197
+ p50Ms: 3,
405198
+ fps: 333,
405199
+ measured: true
405200
+ },
405201
+ "yolov9s-320": {
405202
+ p50Ms: 1.5,
405203
+ fps: 667,
405204
+ measured: true
405205
+ },
405206
+ "yolov9s-640": {
405207
+ p50Ms: 5.1,
405208
+ fps: 196,
405209
+ measured: true
405210
+ },
405211
+ "yolov9m-320": {
405212
+ p50Ms: 3.2,
405213
+ fps: 312,
405214
+ measured: true
405215
+ },
405216
+ "yolov9m-640": {
405217
+ p50Ms: 10.2,
405218
+ fps: 98,
405219
+ measured: true
405220
+ }
404800
405221
  },
404801
- "yolov9m-640": {
404802
- p50Ms: 10.2,
404803
- fps: 98,
404804
- measured: true
404805
- }
404806
- } },
404807
- "apple-m-base": { "apple-ane": {
404808
- "yolov9t-320": {
404809
- p50Ms: 1.32,
404810
- fps: 758,
405222
+ coral: { "yolov9s-320-edgetpu": {
405223
+ p50Ms: 32,
405224
+ fps: 31,
404811
405225
  measured: false
405226
+ } }
405227
+ },
405228
+ "apple-m-base": {
405229
+ "apple-ane": {
405230
+ "yolov9t-320": {
405231
+ p50Ms: 1.32,
405232
+ fps: 758,
405233
+ measured: false
405234
+ },
405235
+ "yolov9t-640": {
405236
+ p50Ms: 3.45,
405237
+ fps: 290,
405238
+ measured: true
405239
+ },
405240
+ "yolov9s-320": {
405241
+ p50Ms: 1.8,
405242
+ fps: 556,
405243
+ measured: false
405244
+ },
405245
+ "yolov9s-640": {
405246
+ p50Ms: 5.46,
405247
+ fps: 183,
405248
+ measured: true
405249
+ },
405250
+ "yolov9m-320": {
405251
+ p50Ms: 3.79,
405252
+ fps: 264,
405253
+ measured: true
405254
+ },
405255
+ "yolov9m-640": {
405256
+ p50Ms: 10.34,
405257
+ fps: 97,
405258
+ measured: true
405259
+ }
404812
405260
  },
404813
- "yolov9t-640": {
404814
- p50Ms: 3.45,
404815
- fps: 290,
404816
- measured: true
404817
- },
404818
- "yolov9s-320": {
404819
- p50Ms: 1.8,
404820
- fps: 556,
405261
+ coral: { "yolov9s-320-edgetpu": {
405262
+ p50Ms: 32,
405263
+ fps: 31,
404821
405264
  measured: false
404822
- },
404823
- "yolov9s-640": {
404824
- p50Ms: 5.46,
404825
- fps: 183,
404826
- measured: true
404827
- },
404828
- "yolov9m-320": {
404829
- p50Ms: 3.79,
404830
- fps: 264,
404831
- measured: true
404832
- },
404833
- "yolov9m-640": {
404834
- p50Ms: 10.34,
404835
- fps: 97,
404836
- measured: true
404837
- }
404838
- } }
405265
+ } }
405266
+ }
404839
405267
  };
404840
405268
  var DETECTOR_ACCURACY = {
404841
405269
  "yolov9m-320": {
@@ -404888,6 +405316,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
404888
405316
  }
404889
405317
  };
404890
405318
  var AUTO_TUNE_FAMILY = "yolov9";
405319
+ var CORAL_DEFAULT_MODEL_ID = "yolov9s-320-edgetpu";
404891
405320
  var RECALL_TIE = 0.02;
404892
405321
  var DEFAULT_HEADROOM = 0.4;
404893
405322
  var DEFAULT_CAMERA_FPS = 10;
@@ -405039,12 +405468,9 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
405039
405468
  const devices = node.devices.toSorted((a, b) => a.key.localeCompare(b.key));
405040
405469
  const contexts = /* @__PURE__ */ new Map();
405041
405470
  const early = /* @__PURE__ */ new Map();
405471
+ const corals = [];
405042
405472
  for (const device2 of devices) {
405043
405473
  const accelerator = acceleratorKindOf(device2.key, hardware);
405044
- if (accelerator === "coral") {
405045
- early.set(device2.key, device2.enabled ? [{ kind: "coral-untouched" }] : []);
405046
- continue;
405047
- }
405048
405474
  if (!device2.enabled) {
405049
405475
  early.set(device2.key, [{ kind: "device-disabled" }]);
405050
405476
  continue;
@@ -405053,6 +405479,23 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
405053
405479
  early.set(device2.key, [{ kind: "device-unavailable" }]);
405054
405480
  continue;
405055
405481
  }
405482
+ if (accelerator === "coral") {
405483
+ const column2 = overlayMeasured(classTable.coral, measured[device2.key]);
405484
+ const builds = node.formatsByModel["yolov9s-320-edgetpu"] ?? [];
405485
+ if (column2?.["yolov9s-320-edgetpu"] === void 0 || !builds.includes(device2.format)) {
405486
+ early.set(device2.key, [{
405487
+ kind: "no-family-build",
405488
+ format: device2.format
405489
+ }]);
405490
+ continue;
405491
+ }
405492
+ corals.push({
405493
+ device: device2,
405494
+ accelerator,
405495
+ column: column2
405496
+ });
405497
+ continue;
405498
+ }
405056
405499
  const column = accelerator === null ? void 0 : overlayMeasured(classTable[accelerator], measured[device2.key]);
405057
405500
  if (accelerator === null || column === void 0) {
405058
405501
  early.set(device2.key, [{
@@ -405103,6 +405546,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
405103
405546
  kind: "no-family-build",
405104
405547
  format: ctx.device.format
405105
405548
  }]);
405549
+ for (const ctx of corals) {
405550
+ const choice = choiceOn(ctx, CORAL_DEFAULT_MODEL_ID, sizing);
405551
+ if (choice === null) continue;
405552
+ chosenByKey.set(ctx.device.key, choice);
405553
+ reasonsByKey.set(ctx.device.key, deviceReasonsFor(choice, sizing));
405554
+ }
405106
405555
  for (const ctx of cpus) {
405107
405556
  const pick2 = pickForGroup([ctx], node.formatsByModel, sizing);
405108
405557
  const choice = pick2 === null ? null : choiceOn(ctx, pick2.modelId, sizing);
@@ -405147,14 +405596,22 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
405147
405596
  function spareOf(c) {
405148
405597
  return c.spare ?? Number.POSITIVE_INFINITY;
405149
405598
  }
405150
- function electByShare(candidates) {
405151
- if (candidates.length === 0) return { kind: "none" };
405599
+ function loadOf(c) {
405600
+ return Math.max(c.running, 0) + Math.max(c.recent ?? 0, 0);
405601
+ }
405602
+ function scoreShares(candidates) {
405152
405603
  const shareSum = candidates.reduce((s, c) => s + Math.max(c.share, 0), 0);
405153
- const running = candidates.reduce((s, c) => s + Math.max(c.running, 0), 0);
405154
- const scored = candidates.map((candidate) => ({
405604
+ const running = candidates.reduce((s, c) => s + loadOf(c), 0);
405605
+ return candidates.map((candidate) => ({
405155
405606
  candidate,
405156
- deficit: (shareSum > 0 ? Math.max(candidate.share, 0) / shareSum : 1 / candidates.length) * (running + 1) - candidate.running
405607
+ deficit: (shareSum > 0 ? Math.max(candidate.share, 0) / shareSum : 1 / candidates.length) * (running + 1) - loadOf(candidate)
405157
405608
  })).toSorted(byDeficit);
405609
+ }
405610
+ function electByShare(candidates) {
405611
+ if (candidates.length === 0) return { kind: "none" };
405612
+ return electFromScored(scoreShares(candidates));
405613
+ }
405614
+ function electFromScored(scored) {
405158
405615
  const turn = scored[0].candidate;
405159
405616
  if (!turn.capped && !turn.saturated) return {
405160
405617
  kind: "elected",
@@ -405178,6 +405635,42 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
405178
405635
  reason: "share"
405179
405636
  };
405180
405637
  }
405638
+ var SHARE_AFFINITY_TOLERANCE_SESSIONS = 1;
405639
+ function toScoredCandidate(s) {
405640
+ return {
405641
+ key: s.candidate.key,
405642
+ share: s.candidate.share,
405643
+ running: s.candidate.running,
405644
+ recent: s.candidate.recent ?? 0,
405645
+ deficit: s.deficit
405646
+ };
405647
+ }
405648
+ function electByShareWithAffinity(candidates, previousKey, toleranceSessions = 1) {
405649
+ const scored = candidates.length === 0 ? [] : scoreShares(candidates);
405650
+ const plain = scored.length === 0 ? { kind: "none" } : electFromScored(scored);
405651
+ const turnKey = plain.kind === "elected" ? plain.key : null;
405652
+ const result = (election, sticky, affinityReason) => ({
405653
+ election,
405654
+ sticky,
405655
+ affinityReason,
405656
+ previousKey,
405657
+ turnKey,
405658
+ scored: scored.map(toScoredCandidate)
405659
+ });
405660
+ if (previousKey === null) return result(plain, false, "no-previous");
405661
+ if (turnKey === previousKey) return result(plain, true, "previous-is-turn");
405662
+ const previous = scored.find((s) => s.candidate.key === previousKey);
405663
+ if (previous === void 0) return result(plain, false, "previous-ineligible");
405664
+ if (previous.candidate.capped || previous.candidate.saturated) return result(plain, false, "previous-no-room");
405665
+ const elected = scored.find((s) => s.candidate.key === turnKey);
405666
+ if (elected === void 0) return result(plain, false, "share-off");
405667
+ if (previous.deficit >= elected.deficit - toleranceSessions - DEFICIT_EPSILON) return result({
405668
+ kind: "elected",
405669
+ key: previousKey,
405670
+ reason: "share"
405671
+ }, true, "within-one-session");
405672
+ return result(plain, false, "share-off");
405673
+ }
405181
405674
  var DEFAULT_WEIGHT = 1;
405182
405675
  function positive(value) {
405183
405676
  return value !== void 0 && value > 0 ? value : DEFAULT_WEIGHT;
@@ -405232,6 +405725,28 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
405232
405725
  deviceWeights
405233
405726
  };
405234
405727
  }
405728
+ function capacityForModel(node, deviceKey, modelId) {
405729
+ if (node === void 0 || modelId === null) return void 0;
405730
+ return node.devices.filter((d) => d.deviceKey === deviceKey && d.modelId === modelId).toSorted((a, b) => b.measuredAtMs - a.measuredAtMs)[0];
405731
+ }
405732
+ function foreignCapacityCells(recommendations, measured) {
405733
+ const out = [];
405734
+ for (const rec of recommendations) {
405735
+ const node = measured.get(rec.nodeId);
405736
+ if (node === void 0) continue;
405737
+ for (const cell of node.devices) {
405738
+ const runs = rec.devices.find((d) => d.deviceKey === cell.deviceKey)?.chosen?.modelId ?? null;
405739
+ if (cell.modelId === null || cell.modelId === runs) continue;
405740
+ out.push({
405741
+ nodeId: rec.nodeId,
405742
+ deviceKey: cell.deviceKey,
405743
+ measuredModelId: cell.modelId,
405744
+ runsModelId: runs
405745
+ });
405746
+ }
405747
+ }
405748
+ return out;
405749
+ }
405235
405750
  var NO_MEASURED_CAPACITY = { readMeasuredCapacity: async () => /* @__PURE__ */ new Map() };
405236
405751
  function toPercentShares(weights) {
405237
405752
  const total = [...weights.values()].reduce((a, b) => a + b, 0);
@@ -405283,7 +405798,11 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
405283
405798
  if (input.source === "hardware") return fromHardware(null);
405284
405799
  if (!rec.reachable) return fromHardware(null);
405285
405800
  const results = input.measured.get(rec.nodeId);
405286
- const byKey = new Map((results?.devices ?? []).map((d) => [d.deviceKey, d]));
405801
+ const byKey = /* @__PURE__ */ new Map();
405802
+ for (const d of rec.devices) {
405803
+ const row = capacityForModel(results, d.deviceKey, d.chosen?.modelId ?? null);
405804
+ if (row !== void 0) byKey.set(d.deviceKey, row);
405805
+ }
405287
405806
  const usable = rec.devices.filter((d) => d.enabled && d.available);
405288
405807
  const mustCover = rec.devices.filter((d) => d.enabled && d.available && d.chosen !== null);
405289
405808
  const covered = usable.filter((d) => byKey.has(d.deviceKey));
@@ -405504,13 +406023,18 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
405504
406023
  }
405505
406024
  return out;
405506
406025
  }
406026
+ function sessionsOfDeviceCell(kMax) {
406027
+ return Math.max(0, Math.floor(kMax));
406028
+ }
405507
406029
  function measuredCapacityFromResults(rows, nodeIds, ctx) {
405508
406030
  const byNode = /* @__PURE__ */ new Map();
405509
406031
  for (const row of freshResults(rows, ctx)) {
405510
- if (!nodeIds.includes(row.nodeId) || row.cell.phase !== "pipeline") continue;
406032
+ if (!nodeIds.includes(row.nodeId)) continue;
406033
+ const candidate = capacityOfRow(row);
405511
406034
  const devices = byNode.get(row.nodeId) ?? /* @__PURE__ */ new Map();
405512
- const held = devices.get(row.cell.deviceKey);
405513
- if (held === void 0 || row.measuredAtMs > held.measuredAtMs) devices.set(row.cell.deviceKey, row);
406035
+ const key = `${candidate.deviceKey}\0${candidate.modelId ?? ""}`;
406036
+ const held = devices.get(key);
406037
+ if (held === void 0 || betterCapacityCell(candidate, held)) devices.set(key, candidate);
405514
406038
  byNode.set(row.nodeId, devices);
405515
406039
  }
405516
406040
  const out = /* @__PURE__ */ new Map();
@@ -405519,16 +406043,31 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
405519
406043
  if (devices === void 0) continue;
405520
406044
  out.set(nodeId, {
405521
406045
  nodeId,
405522
- devices: [...devices.values()].toSorted((a, b) => a.cell.deviceKey.localeCompare(b.cell.deviceKey)).map((r) => ({
405523
- deviceKey: r.cell.deviceKey,
405524
- maxSessions: r.cell.phase === "pipeline" ? r.cell.maxSessions : 0,
405525
- modelId: r.cell.modelId,
405526
- measuredAtMs: r.measuredAtMs
405527
- }))
406046
+ devices: [...devices.values()].toSorted((a, b) => a.deviceKey.localeCompare(b.deviceKey) || (a.modelId ?? "").localeCompare(b.modelId ?? ""))
405528
406047
  });
405529
406048
  }
405530
406049
  return out;
405531
406050
  }
406051
+ function capacityOfRow(row) {
406052
+ const { cell } = row;
406053
+ return cell.phase === "pipeline" ? {
406054
+ deviceKey: cell.deviceKey,
406055
+ maxSessions: cell.maxSessions,
406056
+ modelId: cell.modelId,
406057
+ measuredAtMs: row.measuredAtMs,
406058
+ phase: "pipeline"
406059
+ } : {
406060
+ deviceKey: cell.deviceKey,
406061
+ maxSessions: sessionsOfDeviceCell(cell.kMax),
406062
+ modelId: cell.modelId,
406063
+ measuredAtMs: row.measuredAtMs,
406064
+ phase: "device"
406065
+ };
406066
+ }
406067
+ function betterCapacityCell(a, b) {
406068
+ if (a.phase !== b.phase) return a.phase === "pipeline";
406069
+ return a.measuredAtMs > b.measuredAtMs;
406070
+ }
405532
406071
  var BENCH_ROOT_STEP_ID = "object-detection";
405533
406072
  function slotAddons(schema, slot) {
405534
406073
  return schema.slots.find((s) => s.id === slot)?.addons ?? [];
@@ -407467,6 +408006,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
407467
408006
  exports.COMPOSITION_ITEM_KEY_RE = require_composition.COMPOSITION_ITEM_KEY_RE;
407468
408007
  exports.CONNECTION_TEST_TIMEOUT_MS = CONNECTION_TEST_TIMEOUT_MS;
407469
408008
  exports.CONTAINER_CHILD_PRIORITY = CONTAINER_CHILD_PRIORITY;
408009
+ exports.CORAL_DEFAULT_MODEL_ID = CORAL_DEFAULT_MODEL_ID;
407470
408010
  exports.CORE_BLOCKS_ADDON_ID = CORE_BLOCKS_ADDON_ID;
407471
408011
  exports.CORE_BLOCK_ADDON_PREFIX = CORE_BLOCK_ADDON_PREFIX;
407472
408012
  exports.CREDENTIAL_TOKEN_AUDIENCE = CREDENTIAL_TOKEN_AUDIENCE;
@@ -407626,9 +408166,13 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
407626
408166
  exports.ConnectionTestInputSchema = ConnectionTestInputSchema;
407627
408167
  exports.ConnectionTestOutcomeSchema = ConnectionTestOutcomeSchema;
407628
408168
  exports.ConnectivityStatusSchema = ConnectivityStatusSchema;
408169
+ exports.ConsumableFormSchema = ConsumableFormSchema;
407629
408170
  exports.ConsumableItemSchema = ConsumableItemSchema;
408171
+ exports.ConsumableKindSchema = ConsumableKindSchema;
408172
+ exports.ConsumableLevelSchema = ConsumableLevelSchema;
407630
408173
  exports.ConsumableRemainingSchema = ConsumableRemainingSchema;
407631
408174
  exports.ConsumableRemainingUnitSchema = ConsumableRemainingUnitSchema;
408175
+ exports.ConsumableStatusSchema = ConsumableStatusSchema;
407632
408176
  exports.ConsumablesStatusSchema = ConsumablesStatusSchema;
407633
408177
  exports.ContactStatusSchema = ContactStatusSchema;
407634
408178
  exports.ControlKindSchema = ControlKindSchema;
@@ -407709,6 +408253,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
407709
408253
  exports.DEVICE_DO_ACTIONS = DEVICE_DO_ACTIONS;
407710
408254
  exports.DEVICE_ENROLLMENT_TTL_MS = DEVICE_ENROLLMENT_TTL_MS;
407711
408255
  exports.DEVICE_HISTORY_ENERGY_KEY = DEVICE_HISTORY_ENERGY_KEY;
408256
+ exports.DEVICE_HISTORY_ORPHAN_DELETE_MAX = DEVICE_HISTORY_ORPHAN_DELETE_MAX;
408257
+ exports.DEVICE_HISTORY_ORPHAN_LIST_MAX = DEVICE_HISTORY_ORPHAN_LIST_MAX;
407712
408258
  exports.DEVICE_HISTORY_QUERY_MANY_MAX_ITEMS = DEVICE_HISTORY_QUERY_MANY_MAX_ITEMS;
407713
408259
  exports.DEVICE_NONCE_TTL_MS = DEVICE_NONCE_TTL_MS;
407714
408260
  exports.DEVICE_PROFILES = DEVICE_PROFILES;
@@ -407768,6 +408314,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
407768
408314
  exports.DeviceExportUnexposeInputSchema = UnexposeInputSchema;
407769
408315
  exports.DeviceFeature = require_err_msg.DeviceFeature;
407770
408316
  exports.DeviceHistoryKindSchema = DeviceHistoryKindSchema;
408317
+ exports.DeviceHistoryOrphanDeleteRefusalSchema = DeviceHistoryOrphanDeleteRefusalSchema;
408318
+ exports.DeviceHistoryOrphanReasonSchema = DeviceHistoryOrphanReasonSchema;
407771
408319
  exports.DeviceHistoryProducerKindSchema = DeviceHistoryProducerKindSchema;
407772
408320
  exports.DeviceHistoryRefusalReasonSchema = DeviceHistoryRefusalReasonSchema;
407773
408321
  exports.DeviceHistoryResolutionSchema = DeviceHistoryResolutionSchema;
@@ -407800,7 +408348,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
407800
408348
  exports.DiscoveredTargetSchema = DiscoveredTargetSchema;
407801
408349
  exports.DishwasherAccessSchema = DishwasherAccessSchema;
407802
408350
  exports.DishwasherAvailabilitySchema = DishwasherAvailabilitySchema;
407803
- exports.DishwasherConsumableLevelSchema = DishwasherConsumableLevelSchema;
407804
408351
  exports.DishwasherOperationStateSchema = DishwasherOperationStateSchema;
407805
408352
  exports.DishwasherOptionDescriptorSchema = DishwasherOptionDescriptorSchema;
407806
408353
  exports.DishwasherOptionsSchema = DishwasherOptionsSchema;
@@ -407961,6 +408508,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
407961
408508
  exports.HISTORY_BUCKET_MS = HISTORY_BUCKET_MS;
407962
408509
  exports.HISTORY_CATALOG = HISTORY_CATALOG;
407963
408510
  exports.HISTORY_DEFAULT_MIN_INTERVAL_MS = require_sleep.HISTORY_DEFAULT_MIN_INTERVAL_MS;
408511
+ exports.HISTORY_ITEM_CATALOG = HISTORY_ITEM_CATALOG;
407964
408512
  exports.HISTORY_NONE_CAPS = HISTORY_NONE_CAPS;
407965
408513
  exports.HISTORY_UNIT_LABELS = require_sleep.HISTORY_UNIT_LABELS;
407966
408514
  exports.HOURS_PER_DAY = HOURS_PER_DAY;
@@ -408849,6 +409397,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
408849
409397
  exports.SERVER_NAME_MAX_LENGTH = SERVER_NAME_MAX_LENGTH;
408850
409398
  exports.SERVER_NAME_SETTING_KEY = SERVER_NAME_SETTING_KEY;
408851
409399
  exports.SESSION_PLACEMENT_SETTING_KEY = SESSION_PLACEMENT_SETTING_KEY;
409400
+ exports.SHARE_AFFINITY_TOLERANCE_SESSIONS = SHARE_AFFINITY_TOLERANCE_SESSIONS;
408852
409401
  exports.SHARE_SOURCE_SETTING_KEY = SHARE_SOURCE_SETTING_KEY;
408853
409402
  exports.SHARE_VIEW_KINDS = require_sleep.SHARE_VIEW_KINDS;
408854
409403
  exports.SHARE_VIEW_MAX_DEVICES = SHARE_VIEW_MAX_DEVICES;
@@ -409013,7 +409562,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
409013
409562
  exports.TRANSCODE_DOWN_MAX_BITRATE_KBPS = TRANSCODE_DOWN_MAX_BITRATE_KBPS;
409014
409563
  exports.TRANSCODE_DOWN_MAX_HEIGHT = TRANSCODE_DOWN_MAX_HEIGHT;
409015
409564
  exports.TamperStatusSchema = TamperStatusSchema;
409016
- exports.TankStatusSchema = TankStatusSchema;
409017
409565
  exports.TargetKindCapsSchema = TargetKindCapsSchema;
409018
409566
  exports.TargetKindLevelSchema = TargetKindLevelSchema;
409019
409567
  exports.TargetKindSchema = TargetKindSchema;
@@ -409285,6 +409833,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
409285
409833
  exports.canonicalIconKey = canonicalIconKey;
409286
409834
  exports.canonicalizeAppCallback = canonicalizeAppCallback;
409287
409835
  exports.capabilityScopesOf = capabilityScopesOf;
409836
+ exports.capacityForModel = capacityForModel;
409288
409837
  exports.capacitySessionsOf = capacitySessionsOf;
409289
409838
  exports.carbonMonoxideCapability = carbonMonoxideCapability;
409290
409839
  exports.cellsToRects = cellsToRects;
@@ -409343,6 +409892,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
409343
409892
  exports.conditionVisibleForKind = conditionVisibleForKind;
409344
409893
  exports.connectionTestCapability = connectionTestCapability;
409345
409894
  exports.connectivityCapability = connectivityCapability;
409895
+ exports.consumableFormOf = consumableFormOf;
409896
+ exports.consumableKindOf = consumableKindOf;
409346
409897
  exports.consumableLowPercentOf = consumableLowPercentOf;
409347
409898
  exports.consumablesCapability = consumablesCapability;
409348
409899
  exports.contactCapability = contactCapability;
@@ -409449,6 +410000,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
409449
410000
  exports.egressTranscodeSharingKey = egressTranscodeSharingKey;
409450
410001
  exports.egressTransportFromRequest = egressTransportFromRequest;
409451
410002
  exports.electByShare = electByShare;
410003
+ exports.electByShareWithAffinity = electByShareWithAffinity;
409452
410004
  exports.eligibleUnderBenchmark = eligibleUnderBenchmark;
409453
410005
  exports.embeddingEncoderCapability = embeddingEncoderCapability;
409454
410006
  exports.embeddingModelFailedError = embeddingModelFailedError;
@@ -409513,6 +410065,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
409513
410065
  exports.findTimezone = findTimezone;
409514
410066
  exports.floodCapability = floodCapability;
409515
410067
  exports.foldSnapshotByFunction = foldSnapshotByFunction;
410068
+ exports.foreignCapacityCells = foreignCapacityCells;
409516
410069
  exports.forgetKnownSecret = require_sleep.forgetKnownSecret;
409517
410070
  exports.formatDeviceRevoker = formatDeviceRevoker;
409518
410071
  exports.formatForRuntime = formatForRuntime;
@@ -409541,6 +410094,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
409541
410094
  exports.heatingValveCapability = heatingValveCapability;
409542
410095
  exports.hfModelUrl = require_sleep.hfModelUrl;
409543
410096
  exports.historyFieldsFor = historyFieldsFor;
410097
+ exports.historyItemFieldPath = require_sleep.historyItemFieldPath;
409544
410098
  exports.htmlToText = htmlToText;
409545
410099
  exports.hubAddressHostname = hubAddressHostname;
409546
410100
  exports.hubMotionNativeProvider = hubMotionNativeProvider;
@@ -409944,6 +410498,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
409944
410498
  exports.sensorTransitionOffered = sensorTransitionOffered;
409945
410499
  exports.servedTemplateVars = servedTemplateVars;
409946
410500
  exports.serverManagementCapability = serverManagementCapability;
410501
+ exports.sessionsOfDeviceCell = sessionsOfDeviceCell;
409947
410502
  exports.setByPath = setByPath;
409948
410503
  exports.settingValuesEqual = settingValuesEqual;
409949
410504
  exports.settingsBelow = settingsBelow;
@@ -594172,7 +594727,8 @@ var require_logging_service = __commonJS({
594172
594727
  ...entry.tags,
594173
594728
  addonId: entry.addonId,
594174
594729
  ...nodeId !== void 0 ? { nodeId } : {},
594175
- ...agentId !== void 0 ? { agentId } : {}
594730
+ ...agentId !== void 0 ? { agentId } : {},
594731
+ ...typeof entry.tags?.nodeId === "string" ? { nodeId: entry.tags.nodeId } : {}
594176
594732
  };
594177
594733
  logger = logger.withTags(mergedTags);
594178
594734
  const level = entry.level;