camstack 1.2.204 → 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-l6zKmHEb.js
24028
- var require_dist_l6zKmHEb = __commonJS({
24029
- "../system/dist/dist-l6zKmHEb.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_l6zKmHEb = __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_l6zKmHEb = __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_l6zKmHEb = __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_l6zKmHEb = __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_l6zKmHEb = __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", [
@@ -76064,6 +76132,30 @@ var require_dist_l6zKmHEb = __commonJS({
76064
76132
  * whether persisting is worth a SQLite commit. */
76065
76133
  volatileStateFields: ["lastChangedAt"]
76066
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
+ ]);
76067
76159
  var ConsumableRemainingUnitSchema = zod.z.enum([
76068
76160
  "days",
76069
76161
  "hours",
@@ -76078,10 +76170,14 @@ var require_dist_l6zKmHEb = __commonJS({
76078
76170
  key: zod.z.string().min(1),
76079
76171
  /** Display name. */
76080
76172
  label: zod.z.string().min(1),
76081
- /** Remaining life % when known (0..100). */
76173
+ /** Remaining life % (`wear`) or fill % (`supply`, `waste`) when known (0..100). */
76082
76174
  level: zod.z.number().min(0).max(100).nullable(),
76083
- /** Discrete state when known (binary mode). */
76084
- 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(),
76085
76181
  /** Ms epoch of the last replace, when known. */
76086
76182
  lastResetAt: zod.z.number().nullable(),
76087
76183
  /** Whether `reset()` is meaningful for this item. */
@@ -76104,7 +76200,7 @@ var require_dist_l6zKmHEb = __commonJS({
76104
76200
  kind: "widget",
76105
76201
  widgetId: "host/consumables-panel",
76106
76202
  tab: "consumables",
76107
- topTab: true,
76203
+ placement: "hero",
76108
76204
  label: "Consumables",
76109
76205
  order: 5
76110
76206
  } },
@@ -76166,9 +76262,37 @@ var require_dist_l6zKmHEb = __commonJS({
76166
76262
  */
76167
76263
  durability: "restored",
76168
76264
  /**
76169
- * 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.
76170
76269
  */
76171
- 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
+ },
76172
76296
  /** Clocks, not content: a re-read that changed no item costs no commit. */
76173
76297
  volatileStateFields: ["lastChangedAt", "lastFetchedAt"]
76174
76298
  };
@@ -76529,11 +76653,6 @@ var require_dist_l6zKmHEb = __commonJS({
76529
76653
  "offline",
76530
76654
  "description-missing"
76531
76655
  ]);
76532
- var DishwasherConsumableLevelSchema = zod.z.enum([
76533
- "ok",
76534
- "low",
76535
- "empty"
76536
- ]);
76537
76656
  var DishwasherStatusSchema = zod.z.object({
76538
76657
  availability: DishwasherAvailabilitySchema,
76539
76658
  /** Coarse state for compact surfaces; `unavailable` whenever the appliance state is not known. */
@@ -76566,8 +76685,6 @@ var require_dist_l6zKmHEb = __commonJS({
76566
76685
  powerOn: zod.z.boolean().nullable(),
76567
76686
  /** The raw power state (`On`, `Off`, `Standby`, …); null when not known. */
76568
76687
  powerState: zod.z.string().nullable(),
76569
- saltLevel: DishwasherConsumableLevelSchema.nullable(),
76570
- rinseAidLevel: DishwasherConsumableLevelSchema.nullable(),
76571
76688
  /**
76572
76689
  * True while the appliance asks for its filter to be cleaned (smart-filter, filter-system or
76573
76690
  * combined care-and-filter reminder raised); false when every such reminder reads clear; null
@@ -84894,16 +85011,6 @@ var require_dist_l6zKmHEb = __commonJS({
84894
85011
  "drying",
84895
85012
  "error"
84896
85013
  ]);
84897
- var TankStatusSchema = zod.z.object({
84898
- /** Numeric fill 0..100 when the hardware reports a percentage; null otherwise. */
84899
- level: zod.z.number().min(0).max(100).nullable(),
84900
- /** Discrete state when the hardware is binary-mode; null otherwise. */
84901
- status: zod.z.enum([
84902
- "ok",
84903
- "low",
84904
- "full"
84905
- ]).nullable()
84906
- });
84907
85014
  var VacuumControlStatusSchema = zod.z.object({
84908
85015
  /** Lifecycle state of the vacuum. */
84909
85016
  state: VacuumStateSchema,
@@ -84915,14 +85022,6 @@ var require_dist_l6zKmHEb = __commonJS({
84915
85022
  fanSpeed: zod.z.string().nullable(),
84916
85023
  /** Speed tokens the hardware accepts — drives the UI selector. */
84917
85024
  availableFanSpeeds: zod.z.array(zod.z.string()),
84918
- /** Clean-water (mop) tank. Null when the hardware has no clean-water tank. */
84919
- cleanWater: TankStatusSchema.nullable(),
84920
- /** Dirty-water (recovery) tank. Null when the hardware has no dirty-water tank. */
84921
- dirtyWater: TankStatusSchema.nullable(),
84922
- /** Detergent tank. Null when the hardware has no detergent tank. */
84923
- detergent: TankStatusSchema.nullable(),
84924
- /** Dust bin. Null when the hardware has no dust bin. */
84925
- dustBin: TankStatusSchema.nullable(),
84926
85025
  /** 0..100 cleaning-completion percentage of the current task, or null. */
84927
85026
  progressPercent: zod.z.number().min(0).max(100).nullable(),
84928
85027
  /** Current error code (0 / null = no error). */
@@ -84984,35 +85083,14 @@ var require_dist_l6zKmHEb = __commonJS({
84984
85083
  */
84985
85084
  durability: "session",
84986
85085
  /**
84987
- * 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.
84988
85088
  */
84989
- history: { fields: {
84990
- batteryLevel: {
84991
- kind: "gauge",
84992
- unit: "%",
84993
- deadband: 2
84994
- },
84995
- "cleanWater.level": {
84996
- kind: "gauge",
84997
- unit: "%",
84998
- deadband: 2
84999
- },
85000
- "dirtyWater.level": {
85001
- kind: "gauge",
85002
- unit: "%",
85003
- deadband: 2
85004
- },
85005
- "detergent.level": {
85006
- kind: "gauge",
85007
- unit: "%",
85008
- deadband: 2
85009
- },
85010
- "dustBin.level": {
85011
- kind: "gauge",
85012
- unit: "%",
85013
- deadband: 2
85014
- }
85015
- } }
85089
+ history: { fields: { batteryLevel: {
85090
+ kind: "gauge",
85091
+ unit: "%",
85092
+ deadband: 2
85093
+ } } }
85016
85094
  };
85017
85095
  var ValveStateSchema = zod.z.enum([
85018
85096
  "open",
@@ -89307,6 +89385,12 @@ var require_dist_l6zKmHEb = __commonJS({
89307
89385
  addonId: null,
89308
89386
  access: "create"
89309
89387
  },
89388
+ "deviceHistory.deleteOrphanSeries": {
89389
+ capName: "device-history",
89390
+ capScope: "system",
89391
+ addonId: null,
89392
+ access: "delete"
89393
+ },
89310
89394
  "deviceHistory.getDeviceEnabled": {
89311
89395
  capName: "device-history",
89312
89396
  capScope: "system",
@@ -89325,6 +89409,12 @@ var require_dist_l6zKmHEb = __commonJS({
89325
89409
  addonId: null,
89326
89410
  access: "view"
89327
89411
  },
89412
+ "deviceHistory.listOrphanSeries": {
89413
+ capName: "device-history",
89414
+ capScope: "system",
89415
+ addonId: null,
89416
+ access: "view"
89417
+ },
89328
89418
  "deviceHistory.listSeries": {
89329
89419
  capName: "device-history",
89330
89420
  capScope: "system",
@@ -99619,33 +99709,11 @@ var require_dist_l6zKmHEb = __commonJS({
99619
99709
  unit: "\xB0C",
99620
99710
  deadband: 0.1
99621
99711
  } },
99622
- "vacuum-control": {
99623
- "batteryLevel": {
99624
- kind: "gauge",
99625
- unit: "%",
99626
- deadband: 2
99627
- },
99628
- "cleanWater.level": {
99629
- kind: "gauge",
99630
- unit: "%",
99631
- deadband: 2
99632
- },
99633
- "dirtyWater.level": {
99634
- kind: "gauge",
99635
- unit: "%",
99636
- deadband: 2
99637
- },
99638
- "detergent.level": {
99639
- kind: "gauge",
99640
- unit: "%",
99641
- deadband: 2
99642
- },
99643
- "dustBin.level": {
99644
- kind: "gauge",
99645
- unit: "%",
99646
- deadband: 2
99647
- }
99648
- },
99712
+ "vacuum-control": { "batteryLevel": {
99713
+ kind: "gauge",
99714
+ unit: "%",
99715
+ deadband: 2
99716
+ } },
99649
99717
  "valve": { "position": {
99650
99718
  kind: "gauge",
99651
99719
  unit: "%",
@@ -99687,6 +99755,36 @@ var require_dist_l6zKmHEb = __commonJS({
99687
99755
  }
99688
99756
  }
99689
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
+ } };
99690
99788
  var G711_SCALE_CORRECTION_DB = {
99691
99789
  PCMU: 20 * Math.log10(4),
99692
99790
  PCMA: 20 * Math.log10(8)
@@ -100173,7 +100271,7 @@ var require_dist_l6zKmHEb = __commonJS({
100173
100271
  ([...RULE_TEXT], [...COMBINED_ALARM]),
100174
100272
  ([...RULE_TEXT], [...DEVICE_EVENTS]),
100175
100273
  ([...RULE_TEXT], [...BATTERY, ...CONSUMABLE]),
100176
- ([...RULE_TEXT], [...CONSUMABLE]),
100274
+ ([...RULE_TEXT], [...CONSUMABLE, ...TANK]),
100177
100275
  ([...RULE_TEXT], [...TANK]),
100178
100276
  [...RULE_TEXT],
100179
100277
  ([...RULE_TEXT], [...UPDATES, ...ALERT_EPISODE]),
@@ -101258,6 +101356,12 @@ var require_dist_l6zKmHEb = __commonJS({
101258
101356
  return HISTORY_DEFAULT_MIN_INTERVAL_MS;
101259
101357
  }
101260
101358
  });
101359
+ Object.defineProperty(exports, "HISTORY_ITEM_CATALOG", {
101360
+ enumerable: true,
101361
+ get: function() {
101362
+ return HISTORY_ITEM_CATALOG;
101363
+ }
101364
+ });
101261
101365
  Object.defineProperty(exports, "HUB_MDNS_SERVICE_TYPE", {
101262
101366
  enumerable: true,
101263
101367
  get: function() {
@@ -102080,6 +102184,12 @@ var require_dist_l6zKmHEb = __commonJS({
102080
102184
  return gbToBytes;
102081
102185
  }
102082
102186
  });
102187
+ Object.defineProperty(exports, "historyItemFieldPath", {
102188
+ enumerable: true,
102189
+ get: function() {
102190
+ return historyItemFieldPath;
102191
+ }
102192
+ });
102083
102193
  Object.defineProperty(exports, "hydrateSchema", {
102084
102194
  enumerable: true,
102085
102195
  get: function() {
@@ -114178,11 +114288,11 @@ ${values.join("\n")}` : `${blockName} :`;
114178
114288
  }
114179
114289
  });
114180
114290
 
114181
- // ../system/dist/device-ca-CGcaSheI.js
114182
- var require_device_ca_CGcaSheI = __commonJS({
114183
- "../system/dist/device-ca-CGcaSheI.js"(exports) {
114291
+ // ../system/dist/device-ca-CC58lmNV.js
114292
+ var require_device_ca_CC58lmNV = __commonJS({
114293
+ "../system/dist/device-ca-CC58lmNV.js"(exports) {
114184
114294
  "use strict";
114185
- var require_dist10 = require_dist_l6zKmHEb();
114295
+ var require_dist10 = require_dist_DuBtzSCh();
114186
114296
  var require_x509_es = require_x509_es_C4cOo48M();
114187
114297
  var node_crypto = __require("crypto");
114188
114298
  var SYSTEM_SECRET_OWNER = "system";
@@ -114640,7 +114750,7 @@ var require_alerts_addon = __commonJS({
114640
114750
  [Symbol.toStringTag]: { value: "Module" }
114641
114751
  });
114642
114752
  require_chunk_Cek0wNdY();
114643
- var require_dist10 = require_dist_l6zKmHEb();
114753
+ var require_dist10 = require_dist_DuBtzSCh();
114644
114754
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
114645
114755
  function nextEpisodeId(input) {
114646
114756
  const { previous } = input;
@@ -116108,7 +116218,7 @@ var require_calendars_addon = __commonJS({
116108
116218
  [Symbol.toStringTag]: { value: "Module" }
116109
116219
  });
116110
116220
  require_chunk_Cek0wNdY();
116111
- var require_dist10 = require_dist_l6zKmHEb();
116221
+ var require_dist10 = require_dist_DuBtzSCh();
116112
116222
  var require_settle_sources = require_settle_sources_Bhsy57y();
116113
116223
  var zod = require_zod();
116114
116224
  var CALENDAR_COLOR_PALETTE = [
@@ -116324,11 +116434,11 @@ var require_calendars = __commonJS({
116324
116434
  }
116325
116435
  });
116326
116436
 
116327
- // ../system/dist/composition-sources-5rpWQhA1.js
116328
- var require_composition_sources_5rpWQhA1 = __commonJS({
116329
- "../system/dist/composition-sources-5rpWQhA1.js"(exports) {
116437
+ // ../system/dist/composition-sources-BGXTiUZm.js
116438
+ var require_composition_sources_BGXTiUZm = __commonJS({
116439
+ "../system/dist/composition-sources-BGXTiUZm.js"(exports) {
116330
116440
  "use strict";
116331
- var require_dist10 = require_dist_l6zKmHEb();
116441
+ var require_dist10 = require_dist_DuBtzSCh();
116332
116442
  function normalizedNativeOwner(owner) {
116333
116443
  const trimmed = owner?.trim();
116334
116444
  return trimmed ? trimmed : null;
@@ -116443,8 +116553,8 @@ var require_composer_addon = __commonJS({
116443
116553
  [Symbol.toStringTag]: { value: "Module" }
116444
116554
  });
116445
116555
  require_chunk_Cek0wNdY();
116446
- var require_dist10 = require_dist_l6zKmHEb();
116447
- var require_composition_sources = require_composition_sources_5rpWQhA1();
116556
+ var require_dist10 = require_dist_DuBtzSCh();
116557
+ var require_composition_sources = require_composition_sources_BGXTiUZm();
116448
116558
  var zod = require_zod();
116449
116559
  function isRecord$2(value) {
116450
116560
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -120254,7 +120364,7 @@ var require_console_logging = __commonJS({
120254
120364
  [Symbol.toStringTag]: { value: "Module" }
120255
120365
  });
120256
120366
  require_chunk_Cek0wNdY();
120257
- var require_dist10 = require_dist_l6zKmHEb();
120367
+ var require_dist10 = require_dist_DuBtzSCh();
120258
120368
  var require_formatter = require_formatter_DqAKDlvN();
120259
120369
  var LEVEL_RANK = {
120260
120370
  debug: 0,
@@ -124918,9 +125028,9 @@ var require_core_blocks_addon = __commonJS({
124918
125028
  "use strict";
124919
125029
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
124920
125030
  var require_chunk = require_chunk_Cek0wNdY();
124921
- var require_dist10 = require_dist_l6zKmHEb();
125031
+ var require_dist10 = require_dist_DuBtzSCh();
124922
125032
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
124923
- var require_composition_sources = require_composition_sources_5rpWQhA1();
125033
+ var require_composition_sources = require_composition_sources_BGXTiUZm();
124924
125034
  var require_prefix_range = require_prefix_range_D_KueXbQ();
124925
125035
  var require_prefix_walk = require_prefix_walk_Dv6MLZlM();
124926
125036
  var zod = require_zod();
@@ -127327,9 +127437,9 @@ var require_event_category_7AwNbPjm = __commonJS({
127327
127437
  }
127328
127438
  });
127329
127439
 
127330
- // ../types/dist/sleep-CD18m9HF.js
127331
- var require_sleep_CD18m9HF = __commonJS({
127332
- "../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) {
127333
127443
  "use strict";
127334
127444
  var require_event_category = require_event_category_7AwNbPjm();
127335
127445
  var zod = require_zod();
@@ -132463,6 +132573,9 @@ var require_sleep_CD18m9HF = __commonJS({
132463
132573
  kbps: "kbps",
132464
132574
  fps: "fps"
132465
132575
  };
132576
+ function historyItemFieldPath(itemsPath, field) {
132577
+ return `${itemsPath}[].${field}`;
132578
+ }
132466
132579
  var HISTORY_DEFAULT_MIN_INTERVAL_MS = 1e4;
132467
132580
  function resolveCapMount(def) {
132468
132581
  if (def.mount) return def.mount;
@@ -132485,7 +132598,11 @@ var require_sleep_CD18m9HF = __commonJS({
132485
132598
  zod.z.literal(4)
132486
132599
  ]).optional(),
132487
132600
  tab: zod.z.string().optional(),
132488
- location: zod.z.enum(["settings", "top-tab"]).optional(),
132601
+ location: zod.z.enum([
132602
+ "settings",
132603
+ "top-tab",
132604
+ "hero"
132605
+ ]).optional(),
132489
132606
  order: zod.z.number().optional(),
132490
132607
  fields: zod.z.array(zod.z.any())
132491
132608
  });
@@ -135083,6 +135200,12 @@ var require_sleep_CD18m9HF = __commonJS({
135083
135200
  return hfModelUrl;
135084
135201
  }
135085
135202
  });
135203
+ Object.defineProperty(exports, "historyItemFieldPath", {
135204
+ enumerable: true,
135205
+ get: function() {
135206
+ return historyItemFieldPath;
135207
+ }
135208
+ });
135086
135209
  Object.defineProperty(exports, "hydrateSchema", {
135087
135210
  enumerable: true,
135088
135211
  get: function() {
@@ -135487,7 +135610,7 @@ var require_addon = __commonJS({
135487
135610
  "../types/dist/addon.js"(exports) {
135488
135611
  "use strict";
135489
135612
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
135490
- var require_sleep = require_sleep_CD18m9HF();
135613
+ var require_sleep = require_sleep_DVovpDRU();
135491
135614
  var require_event_category = require_event_category_7AwNbPjm();
135492
135615
  var require_err_msg = require_err_msg_BhVbr0NX();
135493
135616
  var CAP_INPUT_DEFAULTS = Object.freeze({
@@ -136486,24 +136609,6 @@ var require_addon = __commonJS({
136486
136609
  "path": "powerState",
136487
136610
  "valueType": "string"
136488
136611
  },
136489
- {
136490
- "path": "saltLevel",
136491
- "valueType": "enum",
136492
- "enumValues": [
136493
- "ok",
136494
- "low",
136495
- "empty"
136496
- ]
136497
- },
136498
- {
136499
- "path": "rinseAidLevel",
136500
- "valueType": "enum",
136501
- "enumValues": [
136502
- "ok",
136503
- "low",
136504
- "empty"
136505
- ]
136506
- },
136507
136612
  {
136508
136613
  "path": "filterCleaningDue",
136509
136614
  "valueType": "boolean"
@@ -137529,58 +137634,6 @@ var require_addon = __commonJS({
137529
137634
  "path": "fanSpeed",
137530
137635
  "valueType": "string"
137531
137636
  },
137532
- {
137533
- "path": "cleanWater.level",
137534
- "valueType": "number"
137535
- },
137536
- {
137537
- "path": "cleanWater.status",
137538
- "valueType": "enum",
137539
- "enumValues": [
137540
- "ok",
137541
- "low",
137542
- "full"
137543
- ]
137544
- },
137545
- {
137546
- "path": "dirtyWater.level",
137547
- "valueType": "number"
137548
- },
137549
- {
137550
- "path": "dirtyWater.status",
137551
- "valueType": "enum",
137552
- "enumValues": [
137553
- "ok",
137554
- "low",
137555
- "full"
137556
- ]
137557
- },
137558
- {
137559
- "path": "detergent.level",
137560
- "valueType": "number"
137561
- },
137562
- {
137563
- "path": "detergent.status",
137564
- "valueType": "enum",
137565
- "enumValues": [
137566
- "ok",
137567
- "low",
137568
- "full"
137569
- ]
137570
- },
137571
- {
137572
- "path": "dustBin.level",
137573
- "valueType": "number"
137574
- },
137575
- {
137576
- "path": "dustBin.status",
137577
- "valueType": "enum",
137578
- "enumValues": [
137579
- "ok",
137580
- "low",
137581
- "full"
137582
- ]
137583
- },
137584
137637
  {
137585
137638
  "path": "progressPercent",
137586
137639
  "valueType": "number"
@@ -138145,7 +138198,7 @@ var require_device_manager_addon = __commonJS({
138145
138198
  [Symbol.toStringTag]: { value: "Module" }
138146
138199
  });
138147
138200
  require_chunk_Cek0wNdY();
138148
- var require_dist10 = require_dist_l6zKmHEb();
138201
+ var require_dist10 = require_dist_DuBtzSCh();
138149
138202
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
138150
138203
  var require_prefix_range = require_prefix_range_D_KueXbQ();
138151
138204
  var require_prefix_walk = require_prefix_walk_Dv6MLZlM();
@@ -141083,7 +141136,7 @@ var require_device_manager_addon = __commonJS({
141083
141136
  title: ui.label,
141084
141137
  tab: ui.tab,
141085
141138
  order: ui.order ?? 0,
141086
- ...ui.topTab ? { location: "top-tab" } : {},
141139
+ ...ui.placement === "hero" ? { location: "hero" } : ui.topTab ? { location: "top-tab" } : {},
141087
141140
  fields: [{
141088
141141
  type: "widget",
141089
141142
  key: `${entry.capName}Widget`,
@@ -147217,7 +147270,7 @@ var require_hub_forwarder = __commonJS({
147217
147270
  [Symbol.toStringTag]: { value: "Module" }
147218
147271
  });
147219
147272
  require_chunk_Cek0wNdY();
147220
- var require_dist10 = require_dist_l6zKmHEb();
147273
+ var require_dist10 = require_dist_DuBtzSCh();
147221
147274
  var require_formatter = require_formatter_DqAKDlvN();
147222
147275
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
147223
147276
  var HubForwarderDestination = class {
@@ -147351,7 +147404,7 @@ var require_liveness_monitor_addon = __commonJS({
147351
147404
  "use strict";
147352
147405
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
147353
147406
  require_chunk_Cek0wNdY();
147354
- var require_dist10 = require_dist_l6zKmHEb();
147407
+ var require_dist10 = require_dist_DuBtzSCh();
147355
147408
  var NO_DEVICES = "liveness:no-devices";
147356
147409
  var ALL_OFFLINE = "liveness:all-devices-offline";
147357
147410
  var NO_FOOTAGE_PREFIX = "liveness:no-footage:";
@@ -147714,10 +147767,10 @@ var require_local_auth_addon = __commonJS({
147714
147767
  [Symbol.toStringTag]: { value: "Module" }
147715
147768
  });
147716
147769
  var require_chunk = require_chunk_Cek0wNdY();
147717
- var require_dist10 = require_dist_l6zKmHEb();
147770
+ var require_dist10 = require_dist_DuBtzSCh();
147718
147771
  var require_safe_buffer$1 = require_safe_buffer_D3aoa_7X();
147719
147772
  var require_ms$1 = require_ms_LB3giGoU();
147720
- var require_device_ca = require_device_ca_CGcaSheI();
147773
+ var require_device_ca = require_device_ca_CC58lmNV();
147721
147774
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
147722
147775
  var zod = require_zod();
147723
147776
  var node_crypto = __require("crypto");
@@ -158105,7 +158158,7 @@ var require_loki_logging = __commonJS({
158105
158158
  [Symbol.toStringTag]: { value: "Module" }
158106
158159
  });
158107
158160
  require_chunk_Cek0wNdY();
158108
- var require_dist10 = require_dist_l6zKmHEb();
158161
+ var require_dist10 = require_dist_DuBtzSCh();
158109
158162
  var require_log_read_filter = require_log_read_filter_Bi066jwU();
158110
158163
  var zod = require_zod();
158111
158164
  function sanitizeLabelName(raw) {
@@ -159066,7 +159119,7 @@ var require_native_metrics_addon = __commonJS({
159066
159119
  [Symbol.toStringTag]: { value: "Module" }
159067
159120
  });
159068
159121
  var require_chunk = require_chunk_Cek0wNdY();
159069
- var require_dist10 = require_dist_l6zKmHEb();
159122
+ var require_dist10 = require_dist_DuBtzSCh();
159070
159123
  var node_fs_promises = __require("fs/promises");
159071
159124
  var node_util = __require("util");
159072
159125
  var node_os = __require("os");
@@ -161633,7 +161686,7 @@ var require_device_history_addon = __commonJS({
161633
161686
  [Symbol.toStringTag]: { value: "Module" }
161634
161687
  });
161635
161688
  require_chunk_Cek0wNdY();
161636
- var require_dist10 = require_dist_l6zKmHEb();
161689
+ var require_dist10 = require_dist_DuBtzSCh();
161637
161690
  var FIVE_MINUTES_MS = 5 * 6e4;
161638
161691
  var HOUR_MS = 60 * 6e4;
161639
161692
  var DAY_MS = 24 * HOUR_MS;
@@ -161798,13 +161851,25 @@ var require_device_history_addon = __commonJS({
161798
161851
  }
161799
161852
  return deleted;
161800
161853
  }
161801
- /** 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
+ */
161802
161859
  async purgeSeries(series, now) {
161860
+ const deleted = {
161861
+ raw: 0,
161862
+ "5m": 0,
161863
+ "1h": 0,
161864
+ "1d": 0
161865
+ };
161803
161866
  for (const tier of TIERS) {
161804
161867
  const lo = tier === "raw" || tier === "5m" ? Math.max(series.firstTs - BUCKET_LEAD_MS, now - SHORT_TIER_LOOKBACK_MS) : series.firstTs - BUCKET_LEAD_MS;
161805
- 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]);
161806
161870
  }
161807
161871
  await this.deps.store.deleteSeries(series.id);
161872
+ return deleted;
161808
161873
  }
161809
161874
  /**
161810
161875
  * Delete every series of a DELETED device now (D865). Driven by a tombstone
@@ -162129,13 +162194,13 @@ var require_device_history_addon = __commonJS({
162129
162194
  }
162130
162195
  }
162131
162196
  };
162132
- function isRecord$1(value) {
162197
+ function isRecord$2(value) {
162133
162198
  return typeof value === "object" && value !== null && !Array.isArray(value);
162134
162199
  }
162135
162200
  function readPath(slice, path) {
162136
162201
  let current = slice;
162137
162202
  for (const step of path.split(".")) {
162138
- if (!isRecord$1(current)) return void 0;
162203
+ if (!isRecord$2(current)) return void 0;
162139
162204
  current = current[step];
162140
162205
  }
162141
162206
  return current;
@@ -162157,13 +162222,16 @@ var require_device_history_addon = __commonJS({
162157
162222
  function capSeriesKey(capName, fieldPath) {
162158
162223
  return `${CAP_KEY_PREFIX}${capName}.${fieldPath}`;
162159
162224
  }
162225
+ function capItemSeriesKey(capName, items, field) {
162226
+ return capSeriesKey(capName, require_dist10.historyItemFieldPath(items.path, field));
162227
+ }
162160
162228
  function humanize(capName, fieldPath) {
162161
162229
  const field = fieldPath.replace(/([A-Z])/g, " $1").replace(/\./g, " ").toLowerCase();
162162
162230
  return `${capName.replace(/-/g, " ")} ${field}`;
162163
162231
  }
162164
- function catalogMetrics(catalog, now) {
162165
- return Object.entries(catalog).flatMap(([capName, fields]) => Object.entries(fields).map(([path, spec]) => ({
162166
- key: capSeriesKey(capName, path),
162232
+ function metricOf(key, capName, path, spec, now) {
162233
+ return {
162234
+ key,
162167
162235
  source: "cap",
162168
162236
  ownerAddon: "device-history",
162169
162237
  subjectKind: "device",
@@ -162173,7 +162241,15 @@ var require_device_history_addon = __commonJS({
162173
162241
  ingest: "points",
162174
162242
  declaredAt: now,
162175
162243
  values: spec.kind === "enum" ? spec.values ?? [] : null
162176
- })));
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);
162177
162253
  }
162178
162254
  var MAX_PARKED_DEVICES = 5e3;
162179
162255
  var SEED_CONCURRENCY = 8;
@@ -162198,7 +162274,8 @@ var require_device_history_addon = __commonJS({
162198
162274
  /** One `DeviceStateChanged`. Synchronous: the bus must never wait on us. */
162199
162275
  handleState(deviceId, capName, slice, at) {
162200
162276
  const fields = this.deps.catalog[capName];
162201
- if (fields === void 0) return;
162277
+ const items = this.deps.itemCatalog[capName];
162278
+ if (fields === void 0 && items === void 0) return;
162202
162279
  const { counters, directory } = this.deps;
162203
162280
  counters.eventsSeen += 1;
162204
162281
  if (this.deps.optedOut().has(deviceId)) {
@@ -162214,14 +162291,46 @@ var require_device_history_addon = __commonJS({
162214
162291
  counters.skip("not-a-real-device");
162215
162292
  return;
162216
162293
  }
162217
- 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] ?? {})) {
162218
162299
  if (spec.excludeDeviceTypes?.includes(type) === true) {
162219
- counters.skip("excluded-device-type");
162300
+ if (!reconciling) this.deps.counters.skip("excluded-device-type");
162220
162301
  continue;
162221
162302
  }
162222
- const value = this.readValue(slice, capName, path, spec);
162303
+ const key = capSeriesKey(capName, path);
162304
+ const value = this.readValue(slice, key, path, spec);
162223
162305
  if (value === null) continue;
162224
- 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
+ }
162225
162334
  }
162226
162335
  }
162227
162336
  /**
@@ -162229,7 +162338,7 @@ var require_device_history_addon = __commonJS({
162229
162338
  * `enum` state becomes the ordinal of its value in the metric's list; a value
162230
162339
  * the full list cannot take is refused and counted (`enum-overflow`).
162231
162340
  */
162232
- readValue(slice, capName, path, spec) {
162341
+ readValue(slice, key, path, spec) {
162233
162342
  if (spec.skipWhenSet !== void 0) {
162234
162343
  const gate = readPath(slice, spec.skipWhenSet);
162235
162344
  if (gate !== void 0 && gate !== null) return null;
@@ -162237,20 +162346,19 @@ var require_device_history_addon = __commonJS({
162237
162346
  if (spec.kind !== "enum") return readNumeric(slice, path, spec.kind);
162238
162347
  const raw = readPath(slice, path);
162239
162348
  if (typeof raw !== "string" || raw.length === 0) return null;
162240
- const ordinal = this.deps.registry.ordinalOf(capSeriesKey(capName, path), raw);
162349
+ const ordinal = this.deps.registry.ordinalOf(key, raw);
162241
162350
  if (ordinal === null) this.deps.counters.skip("enum-overflow");
162242
162351
  return ordinal;
162243
162352
  }
162244
- consider(deviceId, capName, path, spec, value, at, reconciling) {
162245
- const key = capSeriesKey(capName, path);
162353
+ consider(deviceId, key, scope, spec, value, at, reconciling) {
162246
162354
  const subject = {
162247
162355
  kind: "device",
162248
162356
  deviceId
162249
162357
  };
162250
- const identity = identityOf(key, `d:${deviceId}`, "");
162358
+ const identity = identityOf(key, `d:${deviceId}`, scope);
162251
162359
  const decision = decideCapture(spec, this.recorded.get(identity) ?? null, value, at, { ignoreFloor: reconciling });
162252
162360
  if (decision.action === "emit") {
162253
- this.emit(identity, key, subject, value, at);
162361
+ this.emit(identity, key, subject, scope, value, at);
162254
162362
  if (reconciling) this.deps.counters.reconciledPoints += 1;
162255
162363
  return;
162256
162364
  }
@@ -162258,6 +162366,7 @@ var require_device_history_addon = __commonJS({
162258
162366
  this.pending.set(identity, {
162259
162367
  key,
162260
162368
  subject,
162369
+ scope,
162261
162370
  spec,
162262
162371
  value
162263
162372
  });
@@ -162266,7 +162375,7 @@ var require_device_history_addon = __commonJS({
162266
162375
  this.pending.delete(identity);
162267
162376
  this.deps.counters.skip(decision.reason);
162268
162377
  }
162269
- emit(identity, key, subject, value, ts) {
162378
+ emit(identity, key, subject, scope, value, ts) {
162270
162379
  this.recorded.set(identity, {
162271
162380
  value,
162272
162381
  ts
@@ -162276,7 +162385,7 @@ var require_device_history_addon = __commonJS({
162276
162385
  this.deps.writer.enqueue({
162277
162386
  key,
162278
162387
  subject,
162279
- scope: "",
162388
+ scope,
162280
162389
  ts,
162281
162390
  value
162282
162391
  });
@@ -162293,7 +162402,7 @@ var require_device_history_addon = __commonJS({
162293
162402
  }
162294
162403
  const notBefore = (recorded?.ts ?? now) + minIntervalOf(p.spec);
162295
162404
  if (now < notBefore) continue;
162296
- this.emit(identity, p.key, p.subject, p.value, notBefore);
162405
+ this.emit(identity, p.key, p.subject, p.scope, p.value, notBefore);
162297
162406
  this.deps.counters.trailingEmitted += 1;
162298
162407
  }
162299
162408
  }
@@ -162341,16 +162450,7 @@ var require_device_history_addon = __commonJS({
162341
162450
  if (!Number.isInteger(deviceId) || optedOut.has(deviceId)) continue;
162342
162451
  const type = this.deps.directory.typeOf(deviceId);
162343
162452
  if (type === void 0 || !isRealDeviceType(type)) continue;
162344
- for (const [capName, fields] of Object.entries(this.deps.catalog)) {
162345
- const slice = caps[capName];
162346
- if (slice === void 0) continue;
162347
- for (const [path, spec] of Object.entries(fields)) {
162348
- if (spec.excludeDeviceTypes?.includes(type) === true) continue;
162349
- const value = this.readValue(slice, capName, path, spec);
162350
- if (value === null) continue;
162351
- this.consider(deviceId, capName, path, spec, value, now, true);
162352
- }
162353
- }
162453
+ for (const [capName, slice] of Object.entries(caps)) this.captureSlice(deviceId, type, capName, slice, now, true);
162354
162454
  }
162355
162455
  }
162356
162456
  /**
@@ -162376,8 +162476,8 @@ var require_device_history_addon = __commonJS({
162376
162476
  return this.pending.size;
162377
162477
  }
162378
162478
  /** The last recorded state of a series, for specs. */
162379
- lastRecorded(key, subjectId) {
162380
- return this.recorded.get(identityOf(key, subjectId, ""));
162479
+ lastRecorded(key, subjectId, scope = "") {
162480
+ return this.recorded.get(identityOf(key, subjectId, scope));
162381
162481
  }
162382
162482
  };
162383
162483
  var FLUSH_INTERVAL_MS = 1e4;
@@ -162796,8 +162896,10 @@ var require_device_history_addon = __commonJS({
162796
162896
  }
162797
162897
  var HistoryQuery = class {
162798
162898
  deps;
162899
+ itemFields;
162799
162900
  constructor(deps) {
162800
162901
  this.deps = deps;
162902
+ this.itemFields = itemFieldsByKey(deps.itemCatalog);
162801
162903
  }
162802
162904
  async query(input) {
162803
162905
  if (input.key === "derived:energy.kwh") return this.queryEnergy(input);
@@ -162985,12 +163087,14 @@ var require_device_history_addon = __commonJS({
162985
163087
  for (const s of registry.forSubject(subjectIdOf(subject))) {
162986
163088
  const metric = registry.metric(s.key);
162987
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);
162988
163092
  infos.push({
162989
163093
  key: s.key,
162990
163094
  scope: s.scope,
162991
163095
  kind: metric.kind,
162992
- unit: await this.unitOf(subject, s, metric.unit, slices),
162993
- 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,
162994
163098
  source: metric.source,
162995
163099
  values: metric.kind === "enum" ? metric.values ?? [] : null,
162996
163100
  firstTs: s.firstTs,
@@ -163011,15 +163115,38 @@ var require_device_history_addon = __commonJS({
163011
163115
  });
163012
163116
  return infos.toSorted((a, b) => a.key.localeCompare(b.key) || a.scope.localeCompare(b.scope));
163013
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
+ }
163014
163143
  /** The metric's fixed unit, else the device's own (a `unitFrom` field), else unknown. */
163015
163144
  async unitOf(subject, series, fixed, slices) {
163016
163145
  if (fixed !== null || subject.kind !== "device" || !series.key.startsWith("cap:")) return fixed;
163017
163146
  const spec = this.specOf(series.key);
163018
163147
  if (spec?.unitFrom === void 0) return null;
163019
163148
  const capName = series.key.slice(CAP_KEY_PREFIX.length, series.key.indexOf("."));
163020
- const pending = slices.get(capName) ?? this.deps.readCapSlice(subject.deviceId, capName).catch(() => null);
163021
- slices.set(capName, pending);
163022
- const slice = await pending;
163149
+ const slice = await this.sliceOf(subject.deviceId, capName, slices);
163023
163150
  return slice === null ? null : readUnit(slice, spec.unitFrom);
163024
163151
  }
163025
163152
  specOf(key) {
@@ -163029,10 +163156,149 @@ var require_device_history_addon = __commonJS({
163029
163156
  return this.deps.catalog[body.slice(0, dot)]?.[body.slice(dot + 1)];
163030
163157
  }
163031
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
+ }
163032
163169
  function withoutRaw(tiers) {
163033
163170
  const rollups = tiers.filter((t) => t !== "raw");
163034
163171
  return rollups.length > 0 ? rollups : ["5m"];
163035
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
+ };
163036
163302
  var HISTORY_NAMESPACE = "device-history";
163037
163303
  var COLLECTIONS = {
163038
163304
  metrics: "metrics",
@@ -163632,6 +163898,17 @@ var require_device_history_addon = __commonJS({
163632
163898
  key: String(seriesId)
163633
163899
  });
163634
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
+ }
163635
163912
  async countRows(tier) {
163636
163913
  return this.door.count.query({
163637
163914
  namespace: HISTORY_NAMESPACE,
@@ -164151,6 +164428,7 @@ var require_device_history_addon = __commonJS({
164151
164428
  query;
164152
164429
  retentionJob;
164153
164430
  departures;
164431
+ orphans;
164154
164432
  optedOutDevices = /* @__PURE__ */ new Set();
164155
164433
  tierCounts = /* @__PURE__ */ new Map();
164156
164434
  startedAt;
@@ -164159,7 +164437,7 @@ var require_device_history_addon = __commonJS({
164159
164437
  ready = false;
164160
164438
  constructor(deps) {
164161
164439
  this.deps = deps;
164162
- const { logger, clock, catalog } = deps;
164440
+ const { logger, clock, catalog, itemCatalog } = deps;
164163
164441
  this.startedAt = clock();
164164
164442
  this.store = new HistoryStore(deps.door);
164165
164443
  this.registry = new SeriesRegistry(this.store);
@@ -164183,6 +164461,7 @@ var require_device_history_addon = __commonJS({
164183
164461
  const optedOut = () => this.optedOutDevices;
164184
164462
  this.capture = new CaptureEngine({
164185
164463
  catalog,
164464
+ itemCatalog,
164186
164465
  registry: this.registry,
164187
164466
  store: this.store,
164188
164467
  directory: this.directory,
@@ -164216,6 +164495,7 @@ var require_device_history_addon = __commonJS({
164216
164495
  clock,
164217
164496
  retention: deps.retention,
164218
164497
  catalog,
164498
+ itemCatalog,
164219
164499
  readCapSlice: deps.readCapSlice
164220
164500
  });
164221
164501
  this.departures = new DepartureTracker(this.store, this.registry, logger);
@@ -164232,11 +164512,20 @@ var require_device_history_addon = __commonJS({
164232
164512
  counters: this.counters,
164233
164513
  logger
164234
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
+ });
164235
164524
  }
164236
164525
  async prepare() {
164237
164526
  await this.store.declare();
164238
164527
  await this.registry.load();
164239
- 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);
164240
164529
  this.optedOutDevices = await this.store.loadOptOuts();
164241
164530
  await this.departures.load();
164242
164531
  await this.directory.refresh();
@@ -164355,6 +164644,14 @@ var require_device_history_addon = __commonJS({
164355
164644
  this.optedOutDevices = new Set([...this.optedOutDevices].filter((id) => id !== deviceId));
164356
164645
  return { series };
164357
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
+ }
164358
164655
  /** Series held per device, for the admin clean-up listing. */
164359
164656
  seriesByDevice() {
164360
164657
  const out = /* @__PURE__ */ new Map();
@@ -164526,7 +164823,9 @@ var require_device_history_addon = __commonJS({
164526
164823
  recordBuckets: async (input) => this.requireRuntime().ingest.recordBuckets(callerOf(input), input.buckets),
164527
164824
  getDeviceEnabled: async (input) => ({ enabled: this.requireRuntime().isEnabled(input.deviceId) }),
164528
164825
  setDeviceEnabled: async (input) => ({ enabled: await this.requireRuntime().setEnabled(input.deviceId, input.enabled) }),
164529
- 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) })
164530
164829
  };
164531
164830
  this.emitStoreReadiness("starting");
164532
164831
  this.ctx.logger.info("Initialized \u2014 device-history provider registered (store opens on hub link)");
@@ -164562,6 +164861,7 @@ var require_device_history_addon = __commonJS({
164562
164861
  const runtime = new HistoryRuntime({
164563
164862
  door: api.settingsStore,
164564
164863
  catalog: require_dist10.HISTORY_CATALOG,
164864
+ itemCatalog: require_dist10.HISTORY_ITEM_CATALOG,
164565
164865
  deviceSource: {
164566
164866
  listAll: async () => {
164567
164867
  return (await api.deviceManager.listAll.query({ projection: "slim" })).map((d) => ({
@@ -164679,7 +164979,7 @@ var require_filesystem_storage_addon = __commonJS({
164679
164979
  [Symbol.toStringTag]: { value: "Module" }
164680
164980
  });
164681
164981
  var require_chunk = require_chunk_Cek0wNdY();
164682
- var require_dist10 = require_dist_l6zKmHEb();
164982
+ var require_dist10 = require_dist_DuBtzSCh();
164683
164983
  var node_crypto = __require("crypto");
164684
164984
  var node_fs_promises = __require("fs/promises");
164685
164985
  var node_path = __require("path");
@@ -165789,12 +166089,12 @@ var require_lib = __commonJS({
165789
166089
  }
165790
166090
  });
165791
166091
 
165792
- // ../system/dist/wal-maintenance-B6LSQ_OZ.js
165793
- var require_wal_maintenance_B6LSQ_OZ = __commonJS({
165794
- "../system/dist/wal-maintenance-B6LSQ_OZ.js"(exports) {
166092
+ // ../system/dist/wal-maintenance-EK28XXnc.js
166093
+ var require_wal_maintenance_EK28XXnc = __commonJS({
166094
+ "../system/dist/wal-maintenance-EK28XXnc.js"(exports) {
165795
166095
  "use strict";
165796
166096
  var require_chunk = require_chunk_Cek0wNdY();
165797
- var require_dist10 = require_dist_l6zKmHEb();
166097
+ var require_dist10 = require_dist_DuBtzSCh();
165798
166098
  var require_prefix_range = require_prefix_range_D_KueXbQ();
165799
166099
  var node_crypto = __require("crypto");
165800
166100
  var node_module = __require("module");
@@ -169978,9 +170278,9 @@ var require_sqlite_settings_addon = __commonJS({
169978
170278
  [Symbol.toStringTag]: { value: "Module" }
169979
170279
  });
169980
170280
  var require_chunk = require_chunk_Cek0wNdY();
169981
- var require_dist10 = require_dist_l6zKmHEb();
170281
+ var require_dist10 = require_dist_DuBtzSCh();
169982
170282
  var require_retired_settings_keys = require_retired_settings_keys_CGUqX_Oz();
169983
- var require_wal_maintenance = require_wal_maintenance_B6LSQ_OZ();
170283
+ var require_wal_maintenance = require_wal_maintenance_EK28XXnc();
169984
170284
  var require_sqlite_snapshot_protocol = require_sqlite_snapshot_protocol_B9OndqZv();
169985
170285
  var require_wal_checkpoint_policy = require_wal_checkpoint_policy_d2z2qPqx();
169986
170286
  var node_fs_promises = __require("fs/promises");
@@ -171608,7 +171908,7 @@ var require_sqlite_storage = __commonJS({
171608
171908
  });
171609
171909
  require_chunk_Cek0wNdY();
171610
171910
  var require_builtins_sqlite_storage_filesystem_storage_addon = require_filesystem_storage_addon();
171611
- var require_wal_maintenance = require_wal_maintenance_B6LSQ_OZ();
171911
+ var require_wal_maintenance = require_wal_maintenance_EK28XXnc();
171612
171912
  var require_builtins_sqlite_storage_sqlite_settings_addon = require_sqlite_settings_addon();
171613
171913
  var ConfigStore = class {
171614
171914
  db;
@@ -171692,7 +171992,7 @@ var require_storage_orchestrator_addon = __commonJS({
171692
171992
  [Symbol.toStringTag]: { value: "Module" }
171693
171993
  });
171694
171994
  var require_chunk = require_chunk_Cek0wNdY();
171695
- var require_dist10 = require_dist_l6zKmHEb();
171995
+ var require_dist10 = require_dist_DuBtzSCh();
171696
171996
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
171697
171997
  var zod = require_zod();
171698
171998
  var node_crypto = __require("crypto");
@@ -176279,12 +176579,12 @@ var require_storage_orchestrator = __commonJS({
176279
176579
  }
176280
176580
  });
176281
176581
 
176282
- // ../system/dist/agent-https-DJ8OzQOr.js
176283
- var require_agent_https_DJ8OzQOr = __commonJS({
176284
- "../system/dist/agent-https-DJ8OzQOr.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) {
176285
176585
  "use strict";
176286
176586
  var require_chunk = require_chunk_Cek0wNdY();
176287
- var require_dist10 = require_dist_l6zKmHEb();
176587
+ var require_dist10 = require_dist_DuBtzSCh();
176288
176588
  var require_x509_es = require_x509_es_C4cOo48M();
176289
176589
  var zod = require_zod();
176290
176590
  var node_crypto = __require("crypto");
@@ -177342,11 +177642,11 @@ var require_agent_https_DJ8OzQOr = __commonJS({
177342
177642
  }
177343
177643
  });
177344
177644
 
177345
- // ../system/dist/constants-CuO3rcw4.js
177346
- var require_constants_CuO3rcw4 = __commonJS({
177347
- "../system/dist/constants-CuO3rcw4.js"(exports) {
177645
+ // ../system/dist/constants-2mPqJjkR.js
177646
+ var require_constants_2mPqJjkR = __commonJS({
177647
+ "../system/dist/constants-2mPqJjkR.js"(exports) {
177348
177648
  "use strict";
177349
- var MESH_CA_KEY_REF = require_dist_l6zKmHEb().buildSecretRef({
177649
+ var MESH_CA_KEY_REF = require_dist_DuBtzSCh().buildSecretRef({
177350
177650
  owner: "system",
177351
177651
  scope: { kind: "global" },
177352
177652
  fieldPath: "meshCaKey"
@@ -194443,11 +194743,11 @@ var require_system_config_addon = __commonJS({
194443
194743
  [Symbol.toStringTag]: { value: "Module" }
194444
194744
  });
194445
194745
  var require_chunk = require_chunk_Cek0wNdY();
194446
- var require_dist10 = require_dist_l6zKmHEb();
194746
+ var require_dist10 = require_dist_DuBtzSCh();
194447
194747
  var require_safe_buffer$1 = require_safe_buffer_D3aoa_7X();
194448
194748
  var require_x509_es = require_x509_es_C4cOo48M();
194449
- var require_agent_https = require_agent_https_DJ8OzQOr();
194450
- var require_constants$8 = require_constants_CuO3rcw4();
194749
+ var require_agent_https = require_agent_https_5OgOR_eF();
194750
+ var require_constants$8 = require_constants_2mPqJjkR();
194451
194751
  var require_src$1 = require_src_CnAqG6EF();
194452
194752
  var zod = require_zod();
194453
194753
  var node_crypto = __require("crypto");
@@ -275499,7 +275799,7 @@ var require_winston_logging = __commonJS({
275499
275799
  [Symbol.toStringTag]: { value: "Module" }
275500
275800
  });
275501
275801
  var require_chunk = require_chunk_Cek0wNdY();
275502
- var require_dist10 = require_dist_l6zKmHEb();
275802
+ var require_dist10 = require_dist_DuBtzSCh();
275503
275803
  var require_formatter = require_formatter_DqAKDlvN();
275504
275804
  var require_log_file_read_protocol = require_log_file_read_protocol_D0WcLbIV();
275505
275805
  var node_path = __require("path");
@@ -283302,14 +283602,14 @@ var require_lib_8oZrELLm = __commonJS({
283302
283602
  }
283303
283603
  });
283304
283604
 
283305
- // ../system/dist/hub-mdns-ciao-CIno_SNn.js
283306
- var require_hub_mdns_ciao_CIno_SNn = __commonJS({
283307
- "../system/dist/hub-mdns-ciao-CIno_SNn.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) {
283308
283608
  "use strict";
283309
283609
  var require_chunk = require_chunk_Cek0wNdY();
283310
- var require_dist10 = require_dist_l6zKmHEb();
283610
+ var require_dist10 = require_dist_DuBtzSCh();
283311
283611
  var require_x509_es = require_x509_es_C4cOo48M();
283312
- var require_agent_https = require_agent_https_DJ8OzQOr();
283612
+ var require_agent_https = require_agent_https_5OgOR_eF();
283313
283613
  var node_crypto = __require("crypto");
283314
283614
  var _camstack_types_node = require_node();
283315
283615
  var node_fs_promises = __require("fs/promises");
@@ -284321,12 +284621,12 @@ var require_platform_key_source_Di7HrlGR = __commonJS({
284321
284621
  }
284322
284622
  });
284323
284623
 
284324
- // ../system/dist/restore-archive-B71JI-sP.js
284325
- var require_restore_archive_B71JI_sP = __commonJS({
284326
- "../system/dist/restore-archive-B71JI-sP.js"(exports) {
284624
+ // ../system/dist/restore-archive-CxPAuaNs.js
284625
+ var require_restore_archive_CxPAuaNs = __commonJS({
284626
+ "../system/dist/restore-archive-CxPAuaNs.js"(exports) {
284327
284627
  "use strict";
284328
284628
  var require_chunk = require_chunk_Cek0wNdY();
284329
- var require_dist10 = require_dist_l6zKmHEb();
284629
+ var require_dist10 = require_dist_DuBtzSCh();
284330
284630
  var zod = require_zod();
284331
284631
  var node_crypto = __require("crypto");
284332
284632
  var buffer = __require("buffer");
@@ -295567,8 +295867,8 @@ var require_dist3 = __commonJS({
295567
295867
  "use strict";
295568
295868
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
295569
295869
  var require_chunk = require_chunk_Cek0wNdY();
295570
- var require_dist10 = require_dist_l6zKmHEb();
295571
- var require_device_ca = require_device_ca_CGcaSheI();
295870
+ var require_dist10 = require_dist_DuBtzSCh();
295871
+ var require_device_ca = require_device_ca_CC58lmNV();
295572
295872
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
295573
295873
  var require_builtins_alerts_alerts_addon = require_alerts_addon();
295574
295874
  require_alerts();
@@ -295593,21 +295893,21 @@ var require_dist3 = __commonJS({
295593
295893
  var require_builtins_native_metrics_native_metrics_addon = require_native_metrics_addon();
295594
295894
  var require_builtins_device_history_device_history_addon = require_device_history_addon();
295595
295895
  var require_builtins_sqlite_storage_filesystem_storage_addon = require_filesystem_storage_addon();
295596
- var require_wal_maintenance = require_wal_maintenance_B6LSQ_OZ();
295896
+ var require_wal_maintenance = require_wal_maintenance_EK28XXnc();
295597
295897
  var require_builtins_sqlite_storage_sqlite_settings_addon = require_sqlite_settings_addon();
295598
295898
  var require_builtins_sqlite_storage_index = require_sqlite_storage();
295599
295899
  var require_builtins_storage_orchestrator_storage_orchestrator_addon = require_storage_orchestrator_addon();
295600
295900
  require_storage_orchestrator();
295601
- var require_agent_https = require_agent_https_DJ8OzQOr();
295602
- var require_constants15 = require_constants_CuO3rcw4();
295901
+ var require_agent_https = require_agent_https_5OgOR_eF();
295902
+ var require_constants15 = require_constants_2mPqJjkR();
295603
295903
  var require_builtins_system_config_system_config_addon = require_system_config_addon();
295604
295904
  require_system_config();
295605
295905
  var require_builtins_winston_logging_index = require_winston_logging();
295606
295906
  var require_custom_action_registry = require_custom_action_registry_MRyes5j0();
295607
- var require_hub_mdns_ciao = require_hub_mdns_ciao_CIno_SNn();
295907
+ var require_hub_mdns_ciao = require_hub_mdns_ciao_BLsX_2cL();
295608
295908
  var require_platform_key_source = require_platform_key_source_Di7HrlGR();
295609
295909
  var require_resource_monitor = require_resource_monitor_mio9dSCT();
295610
- var require_restore_archive = require_restore_archive_B71JI_sP();
295910
+ var require_restore_archive = require_restore_archive_CxPAuaNs();
295611
295911
  var require_coral_accelerators = require_coral_accelerators_oUXmmbc2();
295612
295912
  var zod = require_zod();
295613
295913
  var node_crypto = __require("crypto");
@@ -319051,7 +319351,7 @@ var require_dist4 = __commonJS({
319051
319351
  "../types/dist/index.js"(exports) {
319052
319352
  "use strict";
319053
319353
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
319054
- var require_sleep = require_sleep_CD18m9HF();
319354
+ var require_sleep = require_sleep_DVovpDRU();
319055
319355
  var require_event_category = require_event_category_7AwNbPjm();
319056
319356
  var require_composition = require_composition_eoYYVfOp();
319057
319357
  var require_node_color2 = require_node_color();
@@ -324553,7 +324853,11 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
324553
324853
  zod.z.literal(4)
324554
324854
  ]).optional(),
324555
324855
  tab: zod.z.string().optional(),
324556
- location: zod.z.enum(["settings", "top-tab"]).optional(),
324856
+ location: zod.z.enum([
324857
+ "settings",
324858
+ "top-tab",
324859
+ "hero"
324860
+ ]).optional(),
324557
324861
  order: zod.z.number().optional(),
324558
324862
  fields: zod.z.array(zod.z.any())
324559
324863
  });
@@ -329899,6 +330203,48 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
329899
330203
  approxBytes: zod.z.number().nullable(),
329900
330204
  countedAt: zod.z.number().nullable()
329901
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
+ });
329902
330248
  var DeviceHistoryStatusSchema = zod.z.object({
329903
330249
  startedAt: zod.z.number(),
329904
330250
  /** The store is declared, series loaded and the first reconcile has answered. */
@@ -330040,7 +330386,24 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
330040
330386
  kind: "mutation",
330041
330387
  auth: "admin"
330042
330388
  }),
330043
- 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
+ })
330044
330407
  }
330045
330408
  };
330046
330409
  var DeviceCaStatusSchema = zod.z.discriminatedUnion("state", [
@@ -370390,6 +370753,41 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
370390
370753
  * whether persisting is worth a SQLite commit. */
370391
370754
  volatileStateFields: ["lastChangedAt"]
370392
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
+ }
370393
370791
  var ConsumableRemainingUnitSchema = zod.z.enum([
370394
370792
  "days",
370395
370793
  "hours",
@@ -370404,10 +370802,14 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
370404
370802
  key: zod.z.string().min(1),
370405
370803
  /** Display name. */
370406
370804
  label: zod.z.string().min(1),
370407
- /** Remaining life % when known (0..100). */
370805
+ /** Remaining life % (`wear`) or fill % (`supply`, `waste`) when known (0..100). */
370408
370806
  level: zod.z.number().min(0).max(100).nullable(),
370409
- /** Discrete state when known (binary mode). */
370410
- 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(),
370411
370813
  /** Ms epoch of the last replace, when known. */
370412
370814
  lastResetAt: zod.z.number().nullable(),
370413
370815
  /** Whether `reset()` is meaningful for this item. */
@@ -370430,7 +370832,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
370430
370832
  kind: "widget",
370431
370833
  widgetId: "host/consumables-panel",
370432
370834
  tab: "consumables",
370433
- topTab: true,
370835
+ placement: "hero",
370434
370836
  label: "Consumables",
370435
370837
  order: 5
370436
370838
  } },
@@ -370492,9 +370894,37 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
370492
370894
  */
370493
370895
  durability: "restored",
370494
370896
  /**
370495
- * 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.
370496
370901
  */
370497
- 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
+ },
370498
370928
  /** Clocks, not content: a re-read that changed no item costs no commit. */
370499
370929
  volatileStateFields: ["lastChangedAt", "lastFetchedAt"]
370500
370930
  };
@@ -370855,11 +371285,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
370855
371285
  "offline",
370856
371286
  "description-missing"
370857
371287
  ]);
370858
- var DishwasherConsumableLevelSchema = zod.z.enum([
370859
- "ok",
370860
- "low",
370861
- "empty"
370862
- ]);
370863
371288
  var DishwasherStatusSchema = zod.z.object({
370864
371289
  availability: DishwasherAvailabilitySchema,
370865
371290
  /** Coarse state for compact surfaces; `unavailable` whenever the appliance state is not known. */
@@ -370892,8 +371317,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
370892
371317
  powerOn: zod.z.boolean().nullable(),
370893
371318
  /** The raw power state (`On`, `Off`, `Standby`, …); null when not known. */
370894
371319
  powerState: zod.z.string().nullable(),
370895
- saltLevel: DishwasherConsumableLevelSchema.nullable(),
370896
- rinseAidLevel: DishwasherConsumableLevelSchema.nullable(),
370897
371320
  /**
370898
371321
  * True while the appliance asks for its filter to be cleaned (smart-filter, filter-system or
370899
371322
  * combined care-and-filter reminder raised); false when every such reminder reads clear; null
@@ -379587,16 +380010,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
379587
380010
  "drying",
379588
380011
  "error"
379589
380012
  ]);
379590
- var TankStatusSchema = zod.z.object({
379591
- /** Numeric fill 0..100 when the hardware reports a percentage; null otherwise. */
379592
- level: zod.z.number().min(0).max(100).nullable(),
379593
- /** Discrete state when the hardware is binary-mode; null otherwise. */
379594
- status: zod.z.enum([
379595
- "ok",
379596
- "low",
379597
- "full"
379598
- ]).nullable()
379599
- });
379600
380013
  var VacuumControlStatusSchema = zod.z.object({
379601
380014
  /** Lifecycle state of the vacuum. */
379602
380015
  state: VacuumStateSchema,
@@ -379608,14 +380021,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
379608
380021
  fanSpeed: zod.z.string().nullable(),
379609
380022
  /** Speed tokens the hardware accepts — drives the UI selector. */
379610
380023
  availableFanSpeeds: zod.z.array(zod.z.string()),
379611
- /** Clean-water (mop) tank. Null when the hardware has no clean-water tank. */
379612
- cleanWater: TankStatusSchema.nullable(),
379613
- /** Dirty-water (recovery) tank. Null when the hardware has no dirty-water tank. */
379614
- dirtyWater: TankStatusSchema.nullable(),
379615
- /** Detergent tank. Null when the hardware has no detergent tank. */
379616
- detergent: TankStatusSchema.nullable(),
379617
- /** Dust bin. Null when the hardware has no dust bin. */
379618
- dustBin: TankStatusSchema.nullable(),
379619
380024
  /** 0..100 cleaning-completion percentage of the current task, or null. */
379620
380025
  progressPercent: zod.z.number().min(0).max(100).nullable(),
379621
380026
  /** Current error code (0 / null = no error). */
@@ -379677,35 +380082,14 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
379677
380082
  */
379678
380083
  durability: "session",
379679
380084
  /**
379680
- * 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.
379681
380087
  */
379682
- history: { fields: {
379683
- batteryLevel: {
379684
- kind: "gauge",
379685
- unit: "%",
379686
- deadband: 2
379687
- },
379688
- "cleanWater.level": {
379689
- kind: "gauge",
379690
- unit: "%",
379691
- deadband: 2
379692
- },
379693
- "dirtyWater.level": {
379694
- kind: "gauge",
379695
- unit: "%",
379696
- deadband: 2
379697
- },
379698
- "detergent.level": {
379699
- kind: "gauge",
379700
- unit: "%",
379701
- deadband: 2
379702
- },
379703
- "dustBin.level": {
379704
- kind: "gauge",
379705
- unit: "%",
379706
- deadband: 2
379707
- }
379708
- } }
380088
+ history: { fields: { batteryLevel: {
380089
+ kind: "gauge",
380090
+ unit: "%",
380091
+ deadband: 2
380092
+ } } }
379709
380093
  };
379710
380094
  var ValveStateSchema = zod.z.enum([
379711
380095
  "open",
@@ -385339,6 +385723,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
385339
385723
  addonId: null,
385340
385724
  access: "create"
385341
385725
  },
385726
+ "deviceHistory.deleteOrphanSeries": {
385727
+ capName: "device-history",
385728
+ capScope: "system",
385729
+ addonId: null,
385730
+ access: "delete"
385731
+ },
385342
385732
  "deviceHistory.getDeviceEnabled": {
385343
385733
  capName: "device-history",
385344
385734
  capScope: "system",
@@ -385357,6 +385747,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
385357
385747
  addonId: null,
385358
385748
  access: "view"
385359
385749
  },
385750
+ "deviceHistory.listOrphanSeries": {
385751
+ capName: "device-history",
385752
+ capScope: "system",
385753
+ addonId: null,
385754
+ access: "view"
385755
+ },
385360
385756
  "deviceHistory.listSeries": {
385361
385757
  capName: "device-history",
385362
385758
  capScope: "system",
@@ -398884,33 +399280,11 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
398884
399280
  unit: "\xB0C",
398885
399281
  deadband: 0.1
398886
399282
  } },
398887
- "vacuum-control": {
398888
- "batteryLevel": {
398889
- kind: "gauge",
398890
- unit: "%",
398891
- deadband: 2
398892
- },
398893
- "cleanWater.level": {
398894
- kind: "gauge",
398895
- unit: "%",
398896
- deadband: 2
398897
- },
398898
- "dirtyWater.level": {
398899
- kind: "gauge",
398900
- unit: "%",
398901
- deadband: 2
398902
- },
398903
- "detergent.level": {
398904
- kind: "gauge",
398905
- unit: "%",
398906
- deadband: 2
398907
- },
398908
- "dustBin.level": {
398909
- kind: "gauge",
398910
- unit: "%",
398911
- deadband: 2
398912
- }
398913
- },
399283
+ "vacuum-control": { "batteryLevel": {
399284
+ kind: "gauge",
399285
+ unit: "%",
399286
+ deadband: 2
399287
+ } },
398914
399288
  "valve": { "position": {
398915
399289
  kind: "gauge",
398916
399290
  unit: "%",
@@ -398952,12 +399326,41 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
398952
399326
  }
398953
399327
  }
398954
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
+ } };
398955
399359
  var HISTORY_NONE_CAPS = [
398956
399360
  "audio-metrics",
398957
399361
  "automation-control",
398958
399362
  "camera-streams",
398959
399363
  "color",
398960
- "consumables",
398961
399364
  "control",
398962
399365
  "day-night",
398963
399366
  "device-discovery",
@@ -399242,7 +399645,9 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
399242
399645
  declareSeries: (input) => dispatch("deviceHistory", "declareSeries", "mutation", input),
399243
399646
  record: (input) => dispatch("deviceHistory", "record", "mutation", input),
399244
399647
  recordBuckets: (input) => dispatch("deviceHistory", "recordBuckets", "mutation", input),
399245
- 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)
399246
399651
  },
399247
399652
  deviceManager: {
399248
399653
  allocateDeviceId: (input) => dispatch("deviceManager", "allocateDeviceId", "mutation", input),
@@ -402332,17 +402737,18 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
402332
402737
  {
402333
402738
  name: "item",
402334
402739
  label: "Consumable",
402740
+ description: "The device's own name for the item: a filter, a brush, the salt, a tank.",
402335
402741
  example: "Filtro",
402336
402742
  group: "system",
402337
402743
  families: ["rule"],
402338
402744
  fields: [...RULE_TEXT],
402339
402745
  kinds: ["system"],
402340
- systemEventKinds: [...CONSUMABLE]
402746
+ systemEventKinds: [...CONSUMABLE, ...TANK]
402341
402747
  },
402342
402748
  {
402343
402749
  name: "tank",
402344
402750
  label: "Tank",
402345
- 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).",
402346
402752
  example: "clean water",
402347
402753
  group: "system",
402348
402754
  families: ["rule"],
@@ -405190,15 +405596,22 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
405190
405596
  function spareOf(c) {
405191
405597
  return c.spare ?? Number.POSITIVE_INFINITY;
405192
405598
  }
405193
- function electByShare(candidates) {
405194
- 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) {
405195
405603
  const shareSum = candidates.reduce((s, c) => s + Math.max(c.share, 0), 0);
405196
- const loadOf = (c) => Math.max(c.running, 0) + Math.max(c.recent ?? 0, 0);
405197
405604
  const running = candidates.reduce((s, c) => s + loadOf(c), 0);
405198
- const scored = candidates.map((candidate) => ({
405605
+ return candidates.map((candidate) => ({
405199
405606
  candidate,
405200
405607
  deficit: (shareSum > 0 ? Math.max(candidate.share, 0) / shareSum : 1 / candidates.length) * (running + 1) - loadOf(candidate)
405201
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) {
405202
405615
  const turn = scored[0].candidate;
405203
405616
  if (!turn.capped && !turn.saturated) return {
405204
405617
  kind: "elected",
@@ -405222,6 +405635,42 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
405222
405635
  reason: "share"
405223
405636
  };
405224
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
+ }
405225
405674
  var DEFAULT_WEIGHT = 1;
405226
405675
  function positive(value) {
405227
405676
  return value !== void 0 && value > 0 ? value : DEFAULT_WEIGHT;
@@ -407717,9 +408166,13 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
407717
408166
  exports.ConnectionTestInputSchema = ConnectionTestInputSchema;
407718
408167
  exports.ConnectionTestOutcomeSchema = ConnectionTestOutcomeSchema;
407719
408168
  exports.ConnectivityStatusSchema = ConnectivityStatusSchema;
408169
+ exports.ConsumableFormSchema = ConsumableFormSchema;
407720
408170
  exports.ConsumableItemSchema = ConsumableItemSchema;
408171
+ exports.ConsumableKindSchema = ConsumableKindSchema;
408172
+ exports.ConsumableLevelSchema = ConsumableLevelSchema;
407721
408173
  exports.ConsumableRemainingSchema = ConsumableRemainingSchema;
407722
408174
  exports.ConsumableRemainingUnitSchema = ConsumableRemainingUnitSchema;
408175
+ exports.ConsumableStatusSchema = ConsumableStatusSchema;
407723
408176
  exports.ConsumablesStatusSchema = ConsumablesStatusSchema;
407724
408177
  exports.ContactStatusSchema = ContactStatusSchema;
407725
408178
  exports.ControlKindSchema = ControlKindSchema;
@@ -407800,6 +408253,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
407800
408253
  exports.DEVICE_DO_ACTIONS = DEVICE_DO_ACTIONS;
407801
408254
  exports.DEVICE_ENROLLMENT_TTL_MS = DEVICE_ENROLLMENT_TTL_MS;
407802
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;
407803
408258
  exports.DEVICE_HISTORY_QUERY_MANY_MAX_ITEMS = DEVICE_HISTORY_QUERY_MANY_MAX_ITEMS;
407804
408259
  exports.DEVICE_NONCE_TTL_MS = DEVICE_NONCE_TTL_MS;
407805
408260
  exports.DEVICE_PROFILES = DEVICE_PROFILES;
@@ -407859,6 +408314,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
407859
408314
  exports.DeviceExportUnexposeInputSchema = UnexposeInputSchema;
407860
408315
  exports.DeviceFeature = require_err_msg.DeviceFeature;
407861
408316
  exports.DeviceHistoryKindSchema = DeviceHistoryKindSchema;
408317
+ exports.DeviceHistoryOrphanDeleteRefusalSchema = DeviceHistoryOrphanDeleteRefusalSchema;
408318
+ exports.DeviceHistoryOrphanReasonSchema = DeviceHistoryOrphanReasonSchema;
407862
408319
  exports.DeviceHistoryProducerKindSchema = DeviceHistoryProducerKindSchema;
407863
408320
  exports.DeviceHistoryRefusalReasonSchema = DeviceHistoryRefusalReasonSchema;
407864
408321
  exports.DeviceHistoryResolutionSchema = DeviceHistoryResolutionSchema;
@@ -407891,7 +408348,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
407891
408348
  exports.DiscoveredTargetSchema = DiscoveredTargetSchema;
407892
408349
  exports.DishwasherAccessSchema = DishwasherAccessSchema;
407893
408350
  exports.DishwasherAvailabilitySchema = DishwasherAvailabilitySchema;
407894
- exports.DishwasherConsumableLevelSchema = DishwasherConsumableLevelSchema;
407895
408351
  exports.DishwasherOperationStateSchema = DishwasherOperationStateSchema;
407896
408352
  exports.DishwasherOptionDescriptorSchema = DishwasherOptionDescriptorSchema;
407897
408353
  exports.DishwasherOptionsSchema = DishwasherOptionsSchema;
@@ -408052,6 +408508,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
408052
408508
  exports.HISTORY_BUCKET_MS = HISTORY_BUCKET_MS;
408053
408509
  exports.HISTORY_CATALOG = HISTORY_CATALOG;
408054
408510
  exports.HISTORY_DEFAULT_MIN_INTERVAL_MS = require_sleep.HISTORY_DEFAULT_MIN_INTERVAL_MS;
408511
+ exports.HISTORY_ITEM_CATALOG = HISTORY_ITEM_CATALOG;
408055
408512
  exports.HISTORY_NONE_CAPS = HISTORY_NONE_CAPS;
408056
408513
  exports.HISTORY_UNIT_LABELS = require_sleep.HISTORY_UNIT_LABELS;
408057
408514
  exports.HOURS_PER_DAY = HOURS_PER_DAY;
@@ -408940,6 +409397,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
408940
409397
  exports.SERVER_NAME_MAX_LENGTH = SERVER_NAME_MAX_LENGTH;
408941
409398
  exports.SERVER_NAME_SETTING_KEY = SERVER_NAME_SETTING_KEY;
408942
409399
  exports.SESSION_PLACEMENT_SETTING_KEY = SESSION_PLACEMENT_SETTING_KEY;
409400
+ exports.SHARE_AFFINITY_TOLERANCE_SESSIONS = SHARE_AFFINITY_TOLERANCE_SESSIONS;
408943
409401
  exports.SHARE_SOURCE_SETTING_KEY = SHARE_SOURCE_SETTING_KEY;
408944
409402
  exports.SHARE_VIEW_KINDS = require_sleep.SHARE_VIEW_KINDS;
408945
409403
  exports.SHARE_VIEW_MAX_DEVICES = SHARE_VIEW_MAX_DEVICES;
@@ -409104,7 +409562,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
409104
409562
  exports.TRANSCODE_DOWN_MAX_BITRATE_KBPS = TRANSCODE_DOWN_MAX_BITRATE_KBPS;
409105
409563
  exports.TRANSCODE_DOWN_MAX_HEIGHT = TRANSCODE_DOWN_MAX_HEIGHT;
409106
409564
  exports.TamperStatusSchema = TamperStatusSchema;
409107
- exports.TankStatusSchema = TankStatusSchema;
409108
409565
  exports.TargetKindCapsSchema = TargetKindCapsSchema;
409109
409566
  exports.TargetKindLevelSchema = TargetKindLevelSchema;
409110
409567
  exports.TargetKindSchema = TargetKindSchema;
@@ -409435,6 +409892,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
409435
409892
  exports.conditionVisibleForKind = conditionVisibleForKind;
409436
409893
  exports.connectionTestCapability = connectionTestCapability;
409437
409894
  exports.connectivityCapability = connectivityCapability;
409895
+ exports.consumableFormOf = consumableFormOf;
409896
+ exports.consumableKindOf = consumableKindOf;
409438
409897
  exports.consumableLowPercentOf = consumableLowPercentOf;
409439
409898
  exports.consumablesCapability = consumablesCapability;
409440
409899
  exports.contactCapability = contactCapability;
@@ -409541,6 +410000,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
409541
410000
  exports.egressTranscodeSharingKey = egressTranscodeSharingKey;
409542
410001
  exports.egressTransportFromRequest = egressTransportFromRequest;
409543
410002
  exports.electByShare = electByShare;
410003
+ exports.electByShareWithAffinity = electByShareWithAffinity;
409544
410004
  exports.eligibleUnderBenchmark = eligibleUnderBenchmark;
409545
410005
  exports.embeddingEncoderCapability = embeddingEncoderCapability;
409546
410006
  exports.embeddingModelFailedError = embeddingModelFailedError;
@@ -409634,6 +410094,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
409634
410094
  exports.heatingValveCapability = heatingValveCapability;
409635
410095
  exports.hfModelUrl = require_sleep.hfModelUrl;
409636
410096
  exports.historyFieldsFor = historyFieldsFor;
410097
+ exports.historyItemFieldPath = require_sleep.historyItemFieldPath;
409637
410098
  exports.htmlToText = htmlToText;
409638
410099
  exports.hubAddressHostname = hubAddressHostname;
409639
410100
  exports.hubMotionNativeProvider = hubMotionNativeProvider;