camstack 1.2.188 → 1.2.190

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-Ct88catN.js
24028
- var require_dist_Ct88catN = __commonJS({
24029
- "../system/dist/dist-Ct88catN.js"(exports) {
24027
+ // ../system/dist/dist-Bi4Ioxop.js
24028
+ var require_dist_Bi4Ioxop = __commonJS({
24029
+ "../system/dist/dist-Bi4Ioxop.js"(exports) {
24030
24030
  "use strict";
24031
24031
  var zod = require_zod();
24032
24032
  __require("crypto");
@@ -24049,6 +24049,7 @@ var require_dist_Ct88catN = __commonJS({
24049
24049
  EventCategory2["AddonRunnerRefused"] = "addon.runner-refused";
24050
24050
  EventCategory2["ProcessStateChanged"] = "process.state-changed";
24051
24051
  EventCategory2["SystemRunnerCheck"] = "system.runner-check";
24052
+ EventCategory2["AlertEpisode"] = "alert.episode";
24052
24053
  EventCategory2["AddonError"] = "addon.error";
24053
24054
  EventCategory2["AddonPageReady"] = "addon.page-ready";
24054
24055
  EventCategory2["AddonWidgetReady"] = "addon.widget-ready";
@@ -32045,7 +32046,14 @@ var require_dist_Ct88catN = __commonJS({
32045
32046
  ]);
32046
32047
  var AlertActionSchema = zod.z.object({
32047
32048
  label: zod.z.string(),
32048
- href: zod.z.string().optional()
32049
+ href: zod.z.string().optional(),
32050
+ /**
32051
+ * How `href` is followed. Absent / `app`: an admin-ui route (client-side).
32052
+ * `server`: a route the HUB serves (an addon HTTP route such as an OAuth
32053
+ * start) — the browser must leave the SPA with a full navigation, because
32054
+ * the SPA has no such route and would bounce to the home page.
32055
+ */
32056
+ navigation: zod.z.enum(["app", "server"]).optional()
32049
32057
  });
32050
32058
  var AlertStatusSchema = zod.z.enum([
32051
32059
  "active",
@@ -38138,7 +38146,7 @@ var require_dist_Ct88catN = __commonJS({
38138
38146
  var deviceAdoptionCapability = {
38139
38147
  name: "device-adoption",
38140
38148
  scope: "system",
38141
- mode: "singleton",
38149
+ mode: "collection",
38142
38150
  status: {
38143
38151
  schema: AdoptionStatusSchema,
38144
38152
  kind: "poll"
@@ -59424,7 +59432,7 @@ var require_dist_Ct88catN = __commonJS({
59424
59432
  auth: "admin"
59425
59433
  }),
59426
59434
  /** List candidate filter chips for an integration via its device-adoption
59427
- * provider — addonId-routed so it never hits the singleton shadow. */
59435
+ * provider — addonId-routed via the collection pin. */
59428
59436
  adoptionListCandidateFilters: method(zod.z.object({
59429
59437
  addonId: zod.z.string(),
59430
59438
  integrationId: zod.z.string()
@@ -62794,6 +62802,9 @@ var require_dist_Ct88catN = __commonJS({
62794
62802
  "server-updated",
62795
62803
  "export-completed",
62796
62804
  "backup-completed",
62805
+ "alert-critical",
62806
+ "alert-error",
62807
+ "alert-critical-resolved",
62797
62808
  "camera-online",
62798
62809
  "camera-offline",
62799
62810
  "camera-disabled",
@@ -71992,6 +72003,18 @@ var require_dist_Ct88catN = __commonJS({
71992
72003
  */
71993
72004
  movers: method(zod.z.object({}), zod.z.array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }),
71994
72005
  /**
72006
+ * May a mover that touches these locations run NOW? One claim per physical
72007
+ * root is owned by the storage orchestrator: a single-location repoint holds
72008
+ * it while it carries its subtree, and a mover (`relocateMedia`,
72009
+ * `relocateFootage`, a migration move) asks here AFTER registering its job and
72010
+ * cancels itself on `clear: false`. The repoint, in turn, refuses while a
72011
+ * registered mover touches its root. Asked, never polled (D3).
72012
+ */
72013
+ moverClearance: method(zod.z.object({ locationIds: zod.z.array(zod.z.string()).readonly() }), zod.z.object({
72014
+ clear: zod.z.boolean(),
72015
+ repointLocationId: zod.z.string().optional()
72016
+ }), { auth: "admin" }),
72017
+ /**
71995
72018
  * What each class's SOURCE still holds, from the archive — never from the
71996
72019
  * resident index (D295). Only classes with something left (or something
71997
72020
  * unknown) are listed, so an empty list means there is nothing to drain and
@@ -76209,35 +76232,139 @@ var require_dist_Ct88catN = __commonJS({
76209
76232
  "error",
76210
76233
  "unavailable"
76211
76234
  ]);
76235
+ var DishwasherOperationStateSchema = zod.z.enum([
76236
+ "Inactive",
76237
+ "Ready",
76238
+ "DelayedStart",
76239
+ "Run",
76240
+ "Pause",
76241
+ "ActionRequired",
76242
+ "Finished",
76243
+ "Error",
76244
+ "Aborting"
76245
+ ]);
76246
+ var DishwasherAvailabilitySchema = zod.z.enum([
76247
+ "available",
76248
+ "offline",
76249
+ "description-missing"
76250
+ ]);
76212
76251
  var DishwasherConsumableLevelSchema = zod.z.enum([
76213
76252
  "ok",
76214
76253
  "low",
76215
76254
  "empty"
76216
76255
  ]);
76217
76256
  var DishwasherStatusSchema = zod.z.object({
76257
+ availability: DishwasherAvailabilitySchema,
76258
+ /** Coarse state for compact surfaces; `unavailable` whenever the appliance state is not known. */
76218
76259
  state: DishwasherStateSchema,
76219
- /** Active program id/name, or null when none selected / unknown. */
76260
+ /** The appliance's operation state as reported; null when not known. */
76261
+ operationState: DishwasherOperationStateSchema.nullable(),
76262
+ /** Feature key of the ACTIVE (running) program, or null when none runs / not known. */
76220
76263
  program: zod.z.string().nullable(),
76221
- /** Program tokens the hardware accepts — drives the UI selector. */
76264
+ /** Feature key of the SELECTED program (what Start would run), or null when none / not known. */
76265
+ selectedProgram: zod.z.string().nullable(),
76266
+ /** Feature keys of the programs the appliance currently offers — drives the UI selector. */
76222
76267
  availablePrograms: zod.z.array(zod.z.string()),
76223
- /** Minutes remaining on the active cycle, or null when not running. */
76224
- remainingMinutes: zod.z.number().int().nonnegative().nullable(),
76268
+ /** Seconds remaining on the active cycle, or null when not running / not known. */
76269
+ remainingSeconds: zod.z.number().int().nonnegative().nullable(),
76270
+ /** Seconds the delayed start is set to (time until the cycle starts), or null. */
76271
+ delayedStartSeconds: zod.z.number().int().nonnegative().nullable(),
76225
76272
  /** Cycle completion 0..100, or null when not applicable. */
76226
76273
  progressPercent: zod.z.number().min(0).max(100).nullable(),
76227
- /** True when the door is reported open. */
76228
- doorOpen: zod.z.boolean(),
76229
- /** True when remote start is allowed (door closed, power on, etc.). */
76230
- remoteStartAllowed: zod.z.boolean(),
76231
- /** Main power / switched-on state. */
76232
- powerOn: zod.z.boolean(),
76274
+ /** True when the door is reported open; null when the door state is not known. */
76275
+ doorOpen: zod.z.boolean().nullable(),
76276
+ /** True when the door is latched (locked) during a cycle; null when not reported. */
76277
+ doorLocked: zod.z.boolean().nullable(),
76278
+ /** True when remote start is allowed (door closed, power on, etc.); null when not known. */
76279
+ remoteStartAllowed: zod.z.boolean().nullable(),
76280
+ /** True when the appliance accepts remote control now; null when not known. */
76281
+ remoteControlActive: zod.z.boolean().nullable(),
76282
+ /** True while somebody operates the appliance at its panel; null when not reported. */
76283
+ localControlActive: zod.z.boolean().nullable(),
76284
+ /** Main power / switched-on state; null when not known. */
76285
+ powerOn: zod.z.boolean().nullable(),
76286
+ /** The raw power state (`On`, `Off`, `Standby`, …); null when not known. */
76287
+ powerState: zod.z.string().nullable(),
76233
76288
  saltLevel: DishwasherConsumableLevelSchema.nullable(),
76234
76289
  rinseAidLevel: DishwasherConsumableLevelSchema.nullable(),
76235
- halfLoad: zod.z.boolean(),
76236
- hygienePlus: zod.z.boolean(),
76290
+ /** Option states: null when the appliance does not expose the option or has not said. */
76291
+ halfLoad: zod.z.boolean().nullable(),
76292
+ hygienePlus: zod.z.boolean().nullable(),
76293
+ /** Extra dry option when the appliance exposes it. */
76294
+ extraDry: zod.z.boolean().nullable(),
76295
+ /** Intensive zone (lower basket) when the appliance exposes it. */
76296
+ intensiveZone: zod.z.boolean().nullable(),
76297
+ /** VarioSpeed / VarioSpeed+ when the appliance exposes it. */
76298
+ varioSpeedPlus: zod.z.boolean().nullable(),
76299
+ /** Child lock (controls disabled on the appliance); null when not known. */
76300
+ childLock: zod.z.boolean().nullable(),
76301
+ /** Feature keys of the events currently raised (salt low, rinse aid low, program finished, …). */
76302
+ activeEvents: zod.z.array(zod.z.string()),
76303
+ /** Numeric error code when the appliance reports one; null otherwise. */
76237
76304
  errorCode: zod.z.number().nullable(),
76305
+ /** Name of the raised alert-level event while the appliance is in error; null otherwise. */
76238
76306
  errorLabel: zod.z.string().nullable(),
76307
+ /** Ms epoch of the last value received from the appliance; 0 = never. */
76239
76308
  lastFetchedAt: zod.z.number()
76240
76309
  });
76310
+ var DeviceIdSchema = zod.z.number().int().nonnegative();
76311
+ var DishwasherValueSchema = zod.z.union([
76312
+ zod.z.boolean(),
76313
+ zod.z.string(),
76314
+ zod.z.number()
76315
+ ]);
76316
+ var DishwasherAccessSchema = zod.z.enum([
76317
+ "none",
76318
+ "read",
76319
+ "readWrite",
76320
+ "writeOnly",
76321
+ "readStatic"
76322
+ ]);
76323
+ var DishwasherOptionDescriptorSchema = zod.z.object({
76324
+ /** Feature key (`Dishcare.Dishwasher.Option.ExtraDry`). */
76325
+ key: zod.z.string(),
76326
+ /** Last segment of the key, for display. */
76327
+ name: zod.z.string(),
76328
+ type: zod.z.enum([
76329
+ "boolean",
76330
+ "number",
76331
+ "enum",
76332
+ "string"
76333
+ ]),
76334
+ access: DishwasherAccessSchema,
76335
+ /** The appliance reports this entity as currently available. */
76336
+ available: zod.z.boolean(),
76337
+ /** Unit the description declares for a number; null when it declares none. */
76338
+ unit: zod.z.string().nullable(),
76339
+ min: zod.z.number().nullable(),
76340
+ max: zod.z.number().nullable(),
76341
+ step: zod.z.number().nullable(),
76342
+ /** Allowed members of an enum; null for other types. */
76343
+ enumValues: zod.z.array(zod.z.string()).nullable(),
76344
+ defaultValue: DishwasherValueSchema.nullable()
76345
+ });
76346
+ var DishwasherProgramSchema = zod.z.object({
76347
+ /** Feature key (`Dishcare.Dishwasher.Program.Eco50`) — what `setProgram` / `start` take. */
76348
+ key: zod.z.string(),
76349
+ name: zod.z.string(),
76350
+ /** The appliance currently offers this program. */
76351
+ available: zod.z.boolean(),
76352
+ /** The options this program lists, with the program-level access / availability / default. */
76353
+ options: zod.z.array(DishwasherOptionDescriptorSchema)
76354
+ });
76355
+ var DishwasherOptionsSchema = zod.z.object({
76356
+ availability: DishwasherAvailabilitySchema,
76357
+ /** What the appliance can be told to do, from the commands its description declares. */
76358
+ supportsStart: zod.z.boolean(),
76359
+ supportsPause: zod.z.boolean(),
76360
+ supportsResume: zod.z.boolean(),
76361
+ supportsStop: zod.z.boolean(),
76362
+ supportsPower: zod.z.boolean(),
76363
+ /** Every program the description lists, with its options. */
76364
+ programs: zod.z.array(DishwasherProgramSchema),
76365
+ /** Every option and setting entity the description lists (programs reference these by key). */
76366
+ options: zod.z.array(DishwasherOptionDescriptorSchema)
76367
+ });
76241
76368
  var dishwasherCapability = {
76242
76369
  name: "dishwasher",
76243
76370
  scope: "device",
@@ -76245,58 +76372,102 @@ var require_dist_Ct88catN = __commonJS({
76245
76372
  mode: "singleton",
76246
76373
  deviceTypes: [DeviceType.Dishwasher],
76247
76374
  methods: {
76248
- start: method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.void(), {
76375
+ /**
76376
+ * Start a program. Without `program` the selected one starts. `options` are
76377
+ * option feature keys; each is checked against the program's constraints.
76378
+ */
76379
+ start: method(zod.z.object({
76380
+ deviceId: DeviceIdSchema,
76381
+ program: zod.z.string().min(1).optional(),
76382
+ options: zod.z.record(zod.z.string(), DishwasherValueSchema).optional()
76383
+ }), zod.z.void(), {
76249
76384
  kind: "mutation",
76250
76385
  auth: "protected"
76251
76386
  }),
76252
- pause: method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.void(), {
76387
+ pause: method(zod.z.object({ deviceId: DeviceIdSchema }), zod.z.void(), {
76253
76388
  kind: "mutation",
76254
76389
  auth: "protected"
76255
76390
  }),
76256
- stop: method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.void(), {
76391
+ resume: method(zod.z.object({ deviceId: DeviceIdSchema }), zod.z.void(), {
76392
+ kind: "mutation",
76393
+ auth: "protected"
76394
+ }),
76395
+ stop: method(zod.z.object({ deviceId: DeviceIdSchema }), zod.z.void(), {
76257
76396
  kind: "mutation",
76258
76397
  auth: "protected"
76259
76398
  }),
76399
+ /** Select (not start) a program; refused when the appliance does not offer it. */
76260
76400
  setProgram: method(zod.z.object({
76261
- deviceId: zod.z.number().int().nonnegative(),
76401
+ deviceId: DeviceIdSchema,
76262
76402
  program: zod.z.string().min(1)
76263
76403
  }), zod.z.void(), {
76264
76404
  kind: "mutation",
76265
76405
  auth: "protected"
76266
76406
  }),
76267
76407
  setPower: method(zod.z.object({
76268
- deviceId: zod.z.number().int().nonnegative(),
76408
+ deviceId: DeviceIdSchema,
76269
76409
  on: zod.z.boolean()
76270
76410
  }), zod.z.void(), {
76271
76411
  kind: "mutation",
76272
76412
  auth: "protected"
76273
76413
  }),
76274
76414
  setHalfLoad: method(zod.z.object({
76275
- deviceId: zod.z.number().int().nonnegative(),
76415
+ deviceId: DeviceIdSchema,
76276
76416
  on: zod.z.boolean()
76277
76417
  }), zod.z.void(), {
76278
76418
  kind: "mutation",
76279
76419
  auth: "protected"
76280
76420
  }),
76281
76421
  setHygienePlus: method(zod.z.object({
76282
- deviceId: zod.z.number().int().nonnegative(),
76422
+ deviceId: DeviceIdSchema,
76423
+ on: zod.z.boolean()
76424
+ }), zod.z.void(), {
76425
+ kind: "mutation",
76426
+ auth: "protected"
76427
+ }),
76428
+ setExtraDry: method(zod.z.object({
76429
+ deviceId: DeviceIdSchema,
76283
76430
  on: zod.z.boolean()
76284
76431
  }), zod.z.void(), {
76285
76432
  kind: "mutation",
76286
76433
  auth: "protected"
76287
76434
  }),
76435
+ setIntensiveZone: method(zod.z.object({
76436
+ deviceId: DeviceIdSchema,
76437
+ on: zod.z.boolean()
76438
+ }), zod.z.void(), {
76439
+ kind: "mutation",
76440
+ auth: "protected"
76441
+ }),
76442
+ setVarioSpeedPlus: method(zod.z.object({
76443
+ deviceId: DeviceIdSchema,
76444
+ on: zod.z.boolean()
76445
+ }), zod.z.void(), {
76446
+ kind: "mutation",
76447
+ auth: "protected"
76448
+ }),
76449
+ setChildLock: method(zod.z.object({
76450
+ deviceId: DeviceIdSchema,
76451
+ on: zod.z.boolean()
76452
+ }), zod.z.void(), {
76453
+ kind: "mutation",
76454
+ auth: "protected"
76455
+ }),
76456
+ /**
76457
+ * Write any option or setting by feature key. Refused when the description does
76458
+ * not list it, it is not writable or not available now, or the value is outside
76459
+ * its declared range / members.
76460
+ */
76288
76461
  setOption: method(zod.z.object({
76289
- deviceId: zod.z.number().int().nonnegative(),
76462
+ deviceId: DeviceIdSchema,
76290
76463
  key: zod.z.string().min(1),
76291
- value: zod.z.union([
76292
- zod.z.boolean(),
76293
- zod.z.string(),
76294
- zod.z.number()
76295
- ])
76464
+ value: DishwasherValueSchema
76296
76465
  }), zod.z.void(), {
76297
76466
  kind: "mutation",
76298
76467
  auth: "protected"
76299
- })
76468
+ }),
76469
+ /** What this appliance can do, from its own description — the UI is derived from it. */
76470
+ getOptions: method(zod.z.object({ deviceId: DeviceIdSchema }), DishwasherOptionsSchema, { auth: "protected" })
76300
76471
  },
76301
76472
  status: {
76302
76473
  schema: DishwasherStatusSchema,
@@ -86887,6 +87058,7 @@ var require_dist_Ct88catN = __commonJS({
86887
87058
  "fan",
86888
87059
  "vacuum",
86889
87060
  "lawn-mower",
87061
+ "dishwasher",
86890
87062
  "light",
86891
87063
  "switch",
86892
87064
  "siren",
@@ -89625,6 +89797,12 @@ var require_dist_Ct88catN = __commonJS({
89625
89797
  addonId: null,
89626
89798
  access: "view"
89627
89799
  },
89800
+ "dishwasher.getOptions": {
89801
+ capName: "dishwasher",
89802
+ capScope: "device",
89803
+ addonId: null,
89804
+ access: "view"
89805
+ },
89628
89806
  "dishwasher.getStatus": {
89629
89807
  capName: "dishwasher",
89630
89808
  capScope: "device",
@@ -89637,6 +89815,24 @@ var require_dist_Ct88catN = __commonJS({
89637
89815
  addonId: null,
89638
89816
  access: "create"
89639
89817
  },
89818
+ "dishwasher.resume": {
89819
+ capName: "dishwasher",
89820
+ capScope: "device",
89821
+ addonId: null,
89822
+ access: "create"
89823
+ },
89824
+ "dishwasher.setChildLock": {
89825
+ capName: "dishwasher",
89826
+ capScope: "device",
89827
+ addonId: null,
89828
+ access: "create"
89829
+ },
89830
+ "dishwasher.setExtraDry": {
89831
+ capName: "dishwasher",
89832
+ capScope: "device",
89833
+ addonId: null,
89834
+ access: "create"
89835
+ },
89640
89836
  "dishwasher.setHalfLoad": {
89641
89837
  capName: "dishwasher",
89642
89838
  capScope: "device",
@@ -89649,6 +89845,12 @@ var require_dist_Ct88catN = __commonJS({
89649
89845
  addonId: null,
89650
89846
  access: "create"
89651
89847
  },
89848
+ "dishwasher.setIntensiveZone": {
89849
+ capName: "dishwasher",
89850
+ capScope: "device",
89851
+ addonId: null,
89852
+ access: "create"
89853
+ },
89652
89854
  "dishwasher.setOption": {
89653
89855
  capName: "dishwasher",
89654
89856
  capScope: "device",
@@ -89667,6 +89869,12 @@ var require_dist_Ct88catN = __commonJS({
89667
89869
  addonId: null,
89668
89870
  access: "create"
89669
89871
  },
89872
+ "dishwasher.setVarioSpeedPlus": {
89873
+ capName: "dishwasher",
89874
+ capScope: "device",
89875
+ addonId: null,
89876
+ access: "create"
89877
+ },
89670
89878
  "dishwasher.start": {
89671
89879
  capName: "dishwasher",
89672
89880
  capScope: "device",
@@ -94113,6 +94321,12 @@ var require_dist_Ct88catN = __commonJS({
94113
94321
  addonId: null,
94114
94322
  access: "view"
94115
94323
  },
94324
+ "storageMigration.moverClearance": {
94325
+ capName: "storage-migration",
94326
+ capScope: "system",
94327
+ addonId: null,
94328
+ access: "view"
94329
+ },
94116
94330
  "storageMigration.movers": {
94117
94331
  capName: "storage-migration",
94118
94332
  capScope: "system",
@@ -96563,6 +96777,11 @@ var require_dist_Ct88catN = __commonJS({
96563
96777
  form: "single",
96564
96778
  optional: false
96565
96779
  }],
96780
+ "dishwasher.getOptions": [{
96781
+ name: "deviceId",
96782
+ form: "single",
96783
+ optional: false
96784
+ }],
96566
96785
  "dishwasher.getStatus": [{
96567
96786
  name: "deviceId",
96568
96787
  form: "single",
@@ -96573,6 +96792,21 @@ var require_dist_Ct88catN = __commonJS({
96573
96792
  form: "single",
96574
96793
  optional: false
96575
96794
  }],
96795
+ "dishwasher.resume": [{
96796
+ name: "deviceId",
96797
+ form: "single",
96798
+ optional: false
96799
+ }],
96800
+ "dishwasher.setChildLock": [{
96801
+ name: "deviceId",
96802
+ form: "single",
96803
+ optional: false
96804
+ }],
96805
+ "dishwasher.setExtraDry": [{
96806
+ name: "deviceId",
96807
+ form: "single",
96808
+ optional: false
96809
+ }],
96576
96810
  "dishwasher.setHalfLoad": [{
96577
96811
  name: "deviceId",
96578
96812
  form: "single",
@@ -96583,6 +96817,11 @@ var require_dist_Ct88catN = __commonJS({
96583
96817
  form: "single",
96584
96818
  optional: false
96585
96819
  }],
96820
+ "dishwasher.setIntensiveZone": [{
96821
+ name: "deviceId",
96822
+ form: "single",
96823
+ optional: false
96824
+ }],
96586
96825
  "dishwasher.setOption": [{
96587
96826
  name: "deviceId",
96588
96827
  form: "single",
@@ -96598,6 +96837,11 @@ var require_dist_Ct88catN = __commonJS({
96598
96837
  form: "single",
96599
96838
  optional: false
96600
96839
  }],
96840
+ "dishwasher.setVarioSpeedPlus": [{
96841
+ name: "deviceId",
96842
+ form: "single",
96843
+ optional: false
96844
+ }],
96601
96845
  "dishwasher.start": [{
96602
96846
  name: "deviceId",
96603
96847
  form: "single",
@@ -99126,6 +99370,9 @@ var require_dist_Ct88catN = __commonJS({
99126
99370
  "wrapper-update-available": "system",
99127
99371
  "backup-completed": "system",
99128
99372
  "export-completed": "system",
99373
+ "alert-critical": "system",
99374
+ "alert-error": "system",
99375
+ "alert-critical-resolved": "system",
99129
99376
  "storage-location-low": "system",
99130
99377
  "storage-location-critical": "system",
99131
99378
  "storage-location-ok": "system",
@@ -99250,6 +99497,16 @@ var require_dist_Ct88catN = __commonJS({
99250
99497
  "storage-location-ok"
99251
99498
  ]
99252
99499
  },
99500
+ {
99501
+ id: "alerts",
99502
+ family: "system",
99503
+ label: "Alerts",
99504
+ kinds: [
99505
+ "alert-critical",
99506
+ "alert-error",
99507
+ "alert-critical-resolved"
99508
+ ]
99509
+ },
99253
99510
  {
99254
99511
  id: "alarm",
99255
99512
  family: "alarm",
@@ -99267,6 +99524,60 @@ var require_dist_Ct88catN = __commonJS({
99267
99524
  return [[descriptor.kind, field]];
99268
99525
  })));
99269
99526
  Object.freeze(Object.keys(NC_SENSOR_RAISED_FIELD_BY_KIND));
99527
+ zod.z.object({
99528
+ phase: zod.z.enum(["raised", "resolved"]),
99529
+ episodeId: zod.z.string().min(1),
99530
+ alertId: zod.z.string().min(1),
99531
+ severity: AlertSeveritySchema,
99532
+ category: zod.z.string(),
99533
+ title: zod.z.string(),
99534
+ message: zod.z.string(),
99535
+ sourceType: zod.z.string().optional(),
99536
+ sourceId: zod.z.string().optional(),
99537
+ nodeId: zod.z.string().optional(),
99538
+ /** The alert's own action link (where the operator acts on it), when it has one. */
99539
+ actionUrl: zod.z.string().optional(),
99540
+ at: zod.z.number()
99541
+ });
99542
+ function isAlertEpisodeOpen(status) {
99543
+ return status !== "completed" && status !== "dismissed";
99544
+ }
99545
+ function isAlertEpisodeSeverity(severity) {
99546
+ return severity === "critical" || severity === "error";
99547
+ }
99548
+ function alertEpisodeId(alert) {
99549
+ const minted = alert.metadata?.["episodeId"];
99550
+ return typeof minted === "string" && minted.length > 0 ? minted : `${alert.id}@${String(alert.createdAt)}`;
99551
+ }
99552
+ function alertNodeId(alert) {
99553
+ const direct = alert.metadata?.["nodeId"];
99554
+ if (typeof direct === "string") return direct;
99555
+ const runner = alert.metadata?.["runner"];
99556
+ if (typeof runner === "object" && runner !== null && "nodeId" in runner) {
99557
+ const id = runner.nodeId;
99558
+ if (typeof id === "string") return id;
99559
+ }
99560
+ return alert.source?.type === "node" ? alert.source.id : void 0;
99561
+ }
99562
+ function alertEpisodePayload(alert, phase, at) {
99563
+ const nodeId = alertNodeId(alert);
99564
+ return {
99565
+ phase,
99566
+ episodeId: alertEpisodeId(alert),
99567
+ alertId: alert.id,
99568
+ severity: alert.severity,
99569
+ category: alert.category,
99570
+ title: alert.title,
99571
+ message: alert.message,
99572
+ ...alert.source !== void 0 ? {
99573
+ sourceType: alert.source.type,
99574
+ sourceId: alert.source.id
99575
+ } : {},
99576
+ ...nodeId !== void 0 ? { nodeId } : {},
99577
+ ...alert.action?.href !== void 0 ? { actionUrl: alert.action.href } : {},
99578
+ at
99579
+ };
99580
+ }
99270
99581
  var RULE_BODY_ONLY = ["body"];
99271
99582
  var RULE_TEXT = ["title", "body"];
99272
99583
  var BATTERY = ["device-battery-low", "device-battery-normal"];
@@ -99276,6 +99587,11 @@ var require_dist_Ct88catN = __commonJS({
99276
99587
  "device-tank-full",
99277
99588
  "device-tank-normal"
99278
99589
  ];
99590
+ var ALERT_EPISODE = [
99591
+ "alert-critical",
99592
+ "alert-error",
99593
+ "alert-critical-resolved"
99594
+ ];
99279
99595
  var UPDATES = [
99280
99596
  "addon-update-available",
99281
99597
  "server-update-available",
@@ -99465,14 +99781,14 @@ var require_dist_Ct88catN = __commonJS({
99465
99781
  ([...RULE_TEXT], [...CONSUMABLE]),
99466
99782
  ([...RULE_TEXT], [...TANK]),
99467
99783
  [...RULE_TEXT],
99468
- ([...RULE_TEXT], [...UPDATES]),
99469
- ([...RULE_TEXT], [...UPDATES]),
99470
- [...RULE_TEXT],
99784
+ ([...RULE_TEXT], [...UPDATES, ...ALERT_EPISODE]),
99785
+ ([...RULE_TEXT], [...UPDATES, ...ALERT_EPISODE]),
99471
99786
  [...RULE_TEXT],
99472
99787
  [...RULE_TEXT],
99473
99788
  [...RULE_TEXT],
99474
99789
  [...RULE_TEXT],
99475
99790
  [...RULE_TEXT],
99791
+ ([...RULE_TEXT], [...ALERT_EPISODE]),
99476
99792
  [...RULE_TEXT],
99477
99793
  [...RULE_TEXT],
99478
99794
  [...RULE_TEXT],
@@ -99490,7 +99806,11 @@ var require_dist_Ct88catN = __commonJS({
99490
99806
  [...RULE_TEXT],
99491
99807
  [...RULE_TEXT],
99492
99808
  [...RULE_TEXT],
99493
- [...RULE_TEXT],
99809
+ ([...RULE_TEXT], [...ALERT_EPISODE]),
99810
+ ([...RULE_TEXT], [...ALERT_EPISODE]),
99811
+ ([...RULE_TEXT], [...ALERT_EPISODE]),
99812
+ ([...RULE_TEXT], [...ALERT_EPISODE]),
99813
+ ([...RULE_TEXT], [...ALERT_EPISODE]),
99494
99814
  [...RULE_TEXT],
99495
99815
  [...RULE_TEXT],
99496
99816
  [...RULE_TEXT],
@@ -100678,6 +100998,18 @@ var require_dist_Ct88catN = __commonJS({
100678
100998
  return addonWidgetsCapability;
100679
100999
  }
100680
101000
  });
101001
+ Object.defineProperty(exports, "alertEpisodeId", {
101002
+ enumerable: true,
101003
+ get: function() {
101004
+ return alertEpisodeId;
101005
+ }
101006
+ });
101007
+ Object.defineProperty(exports, "alertEpisodePayload", {
101008
+ enumerable: true,
101009
+ get: function() {
101010
+ return alertEpisodePayload;
101011
+ }
101012
+ });
100681
101013
  Object.defineProperty(exports, "alertsCapability", {
100682
101014
  enumerable: true,
100683
101015
  get: function() {
@@ -101164,6 +101496,18 @@ var require_dist_Ct88catN = __commonJS({
101164
101496
  return interpolate;
101165
101497
  }
101166
101498
  });
101499
+ Object.defineProperty(exports, "isAlertEpisodeOpen", {
101500
+ enumerable: true,
101501
+ get: function() {
101502
+ return isAlertEpisodeOpen;
101503
+ }
101504
+ });
101505
+ Object.defineProperty(exports, "isAlertEpisodeSeverity", {
101506
+ enumerable: true,
101507
+ get: function() {
101508
+ return isAlertEpisodeSeverity;
101509
+ }
101510
+ });
101167
101511
  Object.defineProperty(exports, "isAppLinkBase", {
101168
101512
  enumerable: true,
101169
101513
  get: function() {
@@ -113278,11 +113622,11 @@ ${values.join("\n")}` : `${blockName} :`;
113278
113622
  }
113279
113623
  });
113280
113624
 
113281
- // ../system/dist/device-ca-75rt3U_U.js
113282
- var require_device_ca_75rt3U_U = __commonJS({
113283
- "../system/dist/device-ca-75rt3U_U.js"(exports) {
113625
+ // ../system/dist/device-ca-B3hUVfYk.js
113626
+ var require_device_ca_B3hUVfYk = __commonJS({
113627
+ "../system/dist/device-ca-B3hUVfYk.js"(exports) {
113284
113628
  "use strict";
113285
- var require_dist10 = require_dist_Ct88catN();
113629
+ var require_dist10 = require_dist_Bi4Ioxop();
113286
113630
  var require_x509_es = require_x509_es_C4cOo48M();
113287
113631
  var node_crypto = __require("crypto");
113288
113632
  var SYSTEM_SECRET_OWNER = "system";
@@ -113697,11 +114041,11 @@ var require_device_ca_75rt3U_U = __commonJS({
113697
114041
  }
113698
114042
  });
113699
114043
 
113700
- // ../system/dist/phase6-declare-gate-C6WqagkC.js
113701
- var require_phase6_declare_gate_C6WqagkC = __commonJS({
113702
- "../system/dist/phase6-declare-gate-C6WqagkC.js"(exports) {
114044
+ // ../system/dist/phase6-declare-gate-DwYAep7y.js
114045
+ var require_phase6_declare_gate_DwYAep7y = __commonJS({
114046
+ "../system/dist/phase6-declare-gate-DwYAep7y.js"(exports) {
113703
114047
  "use strict";
113704
- var require_dist10 = require_dist_Ct88catN();
114048
+ var require_dist10 = require_dist_Bi4Ioxop();
113705
114049
  function legacyTableRowCountFromList(tables, collection) {
113706
114050
  return tables.find((t) => t.name === collection)?.rows ?? 0;
113707
114051
  }
@@ -113795,124 +114139,33 @@ var require_alerts_addon = __commonJS({
113795
114139
  [Symbol.toStringTag]: { value: "Module" }
113796
114140
  });
113797
114141
  require_chunk_Cek0wNdY();
113798
- var require_dist10 = require_dist_Ct88catN();
114142
+ var require_dist10 = require_dist_Bi4Ioxop();
113799
114143
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
113800
- var require_phase6_declare_gate = require_phase6_declare_gate_C6WqagkC();
113801
- var CRITICAL_CATEGORIES = /* @__PURE__ */ new Set([
113802
- "addon.runner-failed",
113803
- "addon.runner-refused",
113804
- "system.runner-check"
113805
- ]);
113806
- function isCriticalPush(alert) {
113807
- return alert.severity === "critical" || CRITICAL_CATEGORIES.has(alert.category);
113808
- }
113809
- function isPushState(value) {
113810
- if (typeof value !== "object" || value === null) return false;
113811
- const rec = value;
113812
- return (rec["state"] === "pending" || rec["state"] === "delivered" || rec["state"] === "disabled") && typeof rec["key"] === "string" && typeof rec["attempts"] === "number";
113813
- }
113814
- function selectPushTargets(targets, policy) {
113815
- const enabled = targets.filter((t) => t.enabled);
113816
- if (policy.targetIds.length > 0) return enabled.filter((t) => policy.targetIds.includes(t.id));
113817
- return enabled.filter((t) => t.ownerUserId === void 0);
113818
- }
113819
- var MAX_DIGEST_LINES = 20;
113820
- function composeDigest(items) {
113821
- const first = items[0];
113822
- if (items.length === 1 && first !== void 0) return {
113823
- title: first.title,
113824
- body: first.line
113825
- };
113826
- const shown = items.slice(0, MAX_DIGEST_LINES).map((i) => i.line);
113827
- const more = items.length - shown.length;
113828
- return {
113829
- title: `${String(items.length)} addons need attention`,
113830
- body: [...shown, ...more > 0 ? [`... and ${String(more)} more`] : []].join("\n")
113831
- };
113832
- }
113833
- async function deliverDigest(input) {
113834
- const out = /* @__PURE__ */ new Map();
113835
- const work = input.items.filter((i) => !(i.previous !== null && i.previous.key === i.key && i.previous.state !== "pending"));
113836
- if (work.length === 0) return out;
113837
- const sameKey = (i) => i.previous !== null && i.previous.key === i.key ? i.previous : null;
113838
- const attemptsOf = (i) => (sameKey(i)?.attempts ?? 0) + 1;
113839
- const sentOf = (i) => sameKey(i)?.sentTo ?? [];
113840
- if (!input.policy.enabled) {
113841
- for (const i of work) out.set(i.alertId, {
113842
- state: "disabled",
113843
- key: i.key,
113844
- attempts: attemptsOf(i)
113845
- });
113846
- return out;
113847
- }
113848
- const fail = (error) => {
113849
- for (const i of work) out.set(i.alertId, {
113850
- state: "pending",
113851
- key: i.key,
113852
- attempts: attemptsOf(i),
113853
- lastError: error,
113854
- ...sentOf(i).length > 0 ? { sentTo: sentOf(i) } : {}
113855
- });
113856
- return out;
113857
- };
113858
- let targets;
113859
- try {
113860
- targets = selectPushTargets(await input.ports.listTargets(), input.policy);
113861
- } catch (err) {
113862
- return fail(`notifiers unavailable: ${err instanceof Error ? err.message : String(err)}`);
113863
- }
113864
- if (targets.length === 0) return fail("no eligible notifier target");
113865
- const sent = new Map(work.map((i) => [i.alertId, [...sentOf(i)]]));
113866
- const errors = new Map(work.map((i) => [i.alertId, []]));
113867
- for (const target of targets) {
113868
- const todo = work.filter((i) => !(sent.get(i.alertId) ?? []).includes(target.id));
113869
- if (todo.length === 0) continue;
113870
- let failure = null;
113871
- try {
113872
- const result = await input.ports.send(target, composeDigest(todo));
113873
- if (!result.success) failure = `${target.id}: ${result.error ?? "send failed"}`;
113874
- } catch (err) {
113875
- failure = `${target.id}: ${err instanceof Error ? err.message : String(err)}`;
113876
- }
113877
- for (const i of todo) if (failure === null) sent.get(i.alertId)?.push(target.id);
113878
- else errors.get(i.alertId)?.push(failure);
113879
- }
113880
- for (const i of work) {
113881
- const sentTo = sent.get(i.alertId) ?? [];
113882
- const complete = targets.every((t) => sentTo.includes(t.id));
113883
- out.set(i.alertId, complete ? {
113884
- state: "delivered",
113885
- key: i.key,
113886
- at: input.now,
113887
- attempts: attemptsOf(i),
113888
- sentTo
113889
- } : {
113890
- state: "pending",
113891
- key: i.key,
113892
- attempts: attemptsOf(i),
113893
- lastError: (errors.get(i.alertId) ?? []).join("; "),
113894
- ...sentTo.length > 0 ? { sentTo } : {}
113895
- });
113896
- }
113897
- return out;
114144
+ var require_phase6_declare_gate = require_phase6_declare_gate_DwYAep7y();
114145
+ function nextEpisodeId(input) {
114146
+ const { previous } = input;
114147
+ if (previous !== null && require_dist10.isAlertEpisodeOpen(previous.status)) {
114148
+ if (input.incomingKey === void 0 || input.incomingKey === previous.episodeKey) return previous.episodeId;
114149
+ }
114150
+ const minted = `${input.alertId}@${String(input.now)}`;
114151
+ return previous !== null && previous.episodeId === minted ? `${minted}+` : minted;
114152
+ }
114153
+ function episodeEdge(previous, next) {
114154
+ const nextOpen = require_dist10.isAlertEpisodeOpen(next.status);
114155
+ const continues = previous !== null && require_dist10.isAlertEpisodeOpen(previous.status) && previous.episodeId === next.episodeId;
114156
+ if (nextOpen) {
114157
+ if (!require_dist10.isAlertEpisodeSeverity(next.severity)) return null;
114158
+ if (!continues || previous === null) return "raised";
114159
+ if (!require_dist10.isAlertEpisodeSeverity(previous.severity)) return "raised";
114160
+ return previous.severity === "error" && next.severity === "critical" ? "raised" : null;
114161
+ }
114162
+ if (previous !== null && require_dist10.isAlertEpisodeOpen(previous.status) && require_dist10.isAlertEpisodeSeverity(previous.severity)) return "resolved";
114163
+ return null;
113898
114164
  }
113899
114165
  function episodeKeyFromMetadata(metadata) {
113900
114166
  const key = metadata?.["episodeKey"];
113901
114167
  return typeof key === "string" ? key : void 0;
113902
114168
  }
113903
- function shouldDropPriorPush(existingStatus) {
113904
- return existingStatus === "completed";
113905
- }
113906
- function alertWithRetainedPush(toStore, existingPush, existingStatus) {
113907
- if (existingPush === void 0 || shouldDropPriorPush(existingStatus)) return toStore;
113908
- return {
113909
- ...toStore,
113910
- metadata: {
113911
- ...toStore.metadata,
113912
- push: existingPush
113913
- }
113914
- };
113915
- }
113916
114169
  function mergeEmitAlert(input) {
113917
114170
  const incomingKey = episodeKeyFromMetadata(input.incoming.metadata);
113918
114171
  const existingKey = input.existing?.episodeKey;
@@ -113920,26 +114173,11 @@ var require_alerts_addon = __commonJS({
113920
114173
  const sameEpisode = incomingKey === void 0 || incomingKey === existingKey;
113921
114174
  const priorCompleted = input.existing.status === "completed";
113922
114175
  if (sameEpisode && !priorCompleted) return {
113923
- alert: {
113924
- ...input.incoming,
113925
- read: true
113926
- },
113927
- queuePush: false
114176
+ ...input.incoming,
114177
+ read: true
113928
114178
  };
113929
114179
  }
113930
- const queuePush = isCriticalPush(input.incoming) && input.incoming.status === "active" && input.incoming.read === false;
113931
- return {
113932
- alert: input.incoming,
113933
- queuePush
113934
- };
113935
- }
113936
- function criticalPushPayload(alert) {
113937
- const key = episodeKeyFromMetadata(alert.metadata) ?? alert.id;
113938
- const pushLine = alert.metadata?.["pushLine"];
113939
- return {
113940
- key,
113941
- line: typeof pushLine === "string" ? pushLine : alert.title
113942
- };
114180
+ return input.incoming;
113943
114181
  }
113944
114182
  function deviceLabelFromEventData(data) {
113945
114183
  const name = stringNonEmpty(data["name"]) ?? stringNonEmpty(data["deviceName"]);
@@ -114049,13 +114287,13 @@ var require_alerts_addon = __commonJS({
114049
114287
  switch (record.state) {
114050
114288
  case "refused":
114051
114289
  return {
114052
- severity: "error",
114290
+ severity: "critical",
114053
114291
  title: "Addon refused to start",
114054
114292
  message: `${where}: refused - ${record.reason ?? "no reason reported"}${record.remedy !== void 0 ? ` ${record.remedy}` : ""}`
114055
114293
  };
114056
114294
  case "failed":
114057
114295
  return {
114058
- severity: "error",
114296
+ severity: "critical",
114059
114297
  title: "Addon crash loop",
114060
114298
  message: `Runner "${where}" gave up${record.crashesInWindow !== void 0 ? ` after ${String(record.crashesInWindow)} crashes` : ""}${record.addonIds.length > 0 ? ` (addons: ${record.addonIds.join(", ")})` : ""} - ${String(record.count)} events`
114061
114299
  };
@@ -114067,22 +114305,6 @@ var require_alerts_addon = __commonJS({
114067
114305
  };
114068
114306
  }
114069
114307
  }
114070
- function isTerminalRunnerState(state) {
114071
- return state === "failed" || state === "refused";
114072
- }
114073
- function hash(text) {
114074
- let h = 5381;
114075
- for (let i = 0; i < text.length; i++) h = (h << 5) + h + text.charCodeAt(i) | 0;
114076
- return (h >>> 0).toString(36);
114077
- }
114078
- function runnerPushKey(record) {
114079
- const cause = record.state === "refused" ? hash(`${record.reason ?? ""}|${record.remedy ?? ""}`) : "breaker";
114080
- return `runner:${record.state}:${String(record.since)}:${cause}`;
114081
- }
114082
- function runnerPushLine(record) {
114083
- const where = record.nodeId === "hub" ? record.runnerId : `${record.runnerId} on ${record.nodeId}`;
114084
- return record.state === "refused" ? `${where}: refused to start. ${record.remedy ?? "See the Addons page."}` : `${where}: stopped after repeated crashes. Restart it from the Addons page.`;
114085
- }
114086
114308
  var import_read_all_rows = require_read_all_rows$1.require_read_all_rows();
114087
114309
  var ALERTS_COLLECTION = "alerts";
114088
114310
  var AlertBodySchema = require_dist10.AlertSchema.omit({ id: true });
@@ -114374,7 +114596,6 @@ var require_alerts_addon = __commonJS({
114374
114596
  }
114375
114597
  };
114376
114598
  var SILENT_TRACKED_CATEGORIES = [
114377
- "addon.started",
114378
114599
  "process.state-changed",
114379
114600
  "addon.load-recovered",
114380
114601
  "system.liveness-recovered",
@@ -114425,21 +114646,25 @@ var require_alerts_addon = __commonJS({
114425
114646
  }
114426
114647
  var DEFAULT_CONFIG = {
114427
114648
  maxAlerts: 500,
114428
- retentionDays: 30,
114429
- criticalPushEnabled: true,
114430
- criticalPushTargetIds: []
114649
+ retentionDays: 30
114431
114650
  };
114432
- function normalizeCriticalPushTargetIds(raw) {
114433
- if (Array.isArray(raw)) return raw.filter((id) => typeof id === "string" && id.trim().length > 0).map((id) => id.trim());
114434
- if (typeof raw === "string" && raw.trim().length > 0) return raw.split(",").map((id) => id.trim()).filter((id) => id.length > 0);
114435
- return [];
114436
- }
114437
- var PUSH_COALESCE_MS = 2e3;
114438
- var PUSH_RECONCILE_ADDONS = /* @__PURE__ */ new Set(["notifiers", "notification-center"]);
114439
114651
  var EVENT_SOURCE = {
114440
114652
  type: "addon",
114441
114653
  id: "alert-center"
114442
114654
  };
114655
+ var ADDONS_PAGE_ACTION = {
114656
+ label: "Open Addons",
114657
+ href: "/system/addons"
114658
+ };
114659
+ function episodeViewOf(alert) {
114660
+ const key = episodeKeyFromMetadata(alert.metadata);
114661
+ return {
114662
+ status: alert.status,
114663
+ severity: alert.severity,
114664
+ episodeId: require_dist10.alertEpisodeId(alert),
114665
+ ...key !== void 0 ? { episodeKey: key } : {}
114666
+ };
114667
+ }
114443
114668
  function toPrunable(record) {
114444
114669
  return {
114445
114670
  id: record.id,
@@ -114450,11 +114675,8 @@ var require_alerts_addon = __commonJS({
114450
114675
  var AlertCenterAddon = class extends require_dist10.BaseAddon {
114451
114676
  alertStore = null;
114452
114677
  unsubscribers = [];
114453
- /** How long a burst of critical findings is gathered into one digest push. */
114454
- pushCoalesceMs;
114455
- constructor(options) {
114678
+ constructor() {
114456
114679
  super({ ...DEFAULT_CONFIG });
114457
- this.pushCoalesceMs = options?.pushCoalesceMs ?? PUSH_COALESCE_MS;
114458
114680
  }
114459
114681
  /**
114460
114682
  * Per `(category, source.id)` counter used to rate-limit chatty
@@ -114485,9 +114707,7 @@ var require_alerts_addon = __commonJS({
114485
114707
  unsubs.push(unsub);
114486
114708
  }
114487
114709
  this.unsubscribers = unsubs;
114488
- this.reconcilePendingPushes();
114489
114710
  this.dismissLegacyStreamCards(void 0);
114490
- this.migrateCriticalPushTargetIds();
114491
114711
  this.ctx.logger.info("Initialized", { meta: { trackedCategoriesCount: TRACKED_CATEGORIES.length } });
114492
114712
  return [{
114493
114713
  capability: require_dist10.alertsCapability,
@@ -114506,46 +114726,29 @@ var require_alerts_addon = __commonJS({
114506
114726
  description: "Configure alert storage and retention.",
114507
114727
  descriptionKey: "server:globalSettings.s_alerts.description",
114508
114728
  columns: 1,
114509
- fields: [
114510
- this.field({
114511
- type: "number",
114512
- key: "maxAlerts",
114513
- label: "Maximum stored alerts",
114514
- labelKey: "server:globalSettings.s_alerts.maxAlerts.label",
114515
- description: "Oldest read alerts are deleted when this limit is exceeded.",
114516
- descriptionKey: "server:globalSettings.s_alerts.maxAlerts.description",
114517
- min: 50,
114518
- max: 5e3,
114519
- step: 50,
114520
- default: 500
114521
- }),
114522
- this.field({
114523
- type: "number",
114524
- key: "retentionDays",
114525
- label: "Alert retention (days)",
114526
- labelKey: "server:globalSettings.s_alerts.retentionDays.label",
114527
- description: "Alerts older than this are automatically removed.",
114528
- descriptionKey: "server:globalSettings.s_alerts.retentionDays.description",
114529
- min: 1,
114530
- max: 365,
114531
- step: 1,
114532
- default: 30
114533
- }),
114534
- this.field({
114535
- type: "boolean",
114536
- key: "criticalPushEnabled",
114537
- label: "Push critical system alerts",
114538
- description: "A runner that failed or refused to start, or a node whose runners are degraded, is pushed straight to the notifier targets \u2014 even when Notification Center is down. This path does not use notification rules.",
114539
- default: true
114540
- }),
114541
- this.field({
114542
- type: "notification-target-multiselect",
114543
- key: "criticalPushTargetIds",
114544
- label: "Critical push targets",
114545
- description: "Notifier targets that receive critical system alerts. Empty = every enabled shared (not personal) target.",
114546
- default: []
114547
- })
114548
- ]
114729
+ fields: [this.field({
114730
+ type: "number",
114731
+ key: "maxAlerts",
114732
+ label: "Maximum stored alerts",
114733
+ labelKey: "server:globalSettings.s_alerts.maxAlerts.label",
114734
+ description: "Oldest read alerts are deleted when this limit is exceeded.",
114735
+ descriptionKey: "server:globalSettings.s_alerts.maxAlerts.description",
114736
+ min: 50,
114737
+ max: 5e3,
114738
+ step: 50,
114739
+ default: 500
114740
+ }), this.field({
114741
+ type: "number",
114742
+ key: "retentionDays",
114743
+ label: "Alert retention (days)",
114744
+ labelKey: "server:globalSettings.s_alerts.retentionDays.label",
114745
+ description: "Alerts older than this are automatically removed.",
114746
+ descriptionKey: "server:globalSettings.s_alerts.retentionDays.description",
114747
+ min: 1,
114748
+ max: 365,
114749
+ step: 1,
114750
+ default: 30
114751
+ })]
114549
114752
  }] });
114550
114753
  }
114551
114754
  async listAlerts(filter) {
@@ -114584,7 +114787,17 @@ var require_alerts_addon = __commonJS({
114584
114787
  updatedAt: Date.now()
114585
114788
  });
114586
114789
  }
114587
- async handleEvent(category, event) {
114790
+ /**
114791
+ * Events are handled one at a time: every handler is a read-modify-write of
114792
+ * an alert, and two concurrent events about one runner would both read "no
114793
+ * alert yet" and open two episodes for it.
114794
+ */
114795
+ eventChain = Promise.resolve();
114796
+ handleEvent(category, event) {
114797
+ this.eventChain = this.eventChain.then(() => this.processEvent(category, event));
114798
+ return this.eventChain;
114799
+ }
114800
+ async processEvent(category, event) {
114588
114801
  try {
114589
114802
  if (category === "model.download.progress") {
114590
114803
  await this.handleModelDownload(event);
@@ -114602,11 +114815,6 @@ var require_alerts_addon = __commonJS({
114602
114815
  await this.handleRunnerCheck(event);
114603
114816
  return;
114604
114817
  }
114605
- if (category === require_dist10.EventCategory.AddonStarted) {
114606
- const addonId = event.data["addonId"];
114607
- if (typeof addonId === "string" && PUSH_RECONCILE_ADDONS.has(addonId)) await this.reconcilePendingPushes();
114608
- return;
114609
- }
114610
114818
  if (category === require_dist10.EventCategory.AddonLoadFailed) {
114611
114819
  await this.handleAddonLoadFailed(event);
114612
114820
  return;
@@ -114707,11 +114915,8 @@ var require_alerts_addon = __commonJS({
114707
114915
  type: "runner",
114708
114916
  id: record.runnerId
114709
114917
  },
114710
- metadata: {
114711
- runner: record,
114712
- pushLine: runnerPushLine(record),
114713
- ...existing?.push !== void 0 ? { push: existing.push } : {}
114714
- }
114918
+ action: ADDONS_PAGE_ACTION,
114919
+ metadata: { runner: record }
114715
114920
  };
114716
114921
  await this.persistAlert(alert);
114717
114922
  if (previous === null) this.emitAlertCreatedEvent(alert);
@@ -114720,17 +114925,11 @@ var require_alerts_addon = __commonJS({
114720
114925
  title: alert.title
114721
114926
  });
114722
114927
  await this.enforceMaxAlerts();
114723
- if (isTerminalRunnerState(record.state)) this.queuePush({
114724
- alertId,
114725
- key: runnerPushKey(record),
114726
- title: rendered.title,
114727
- line: runnerPushLine(record)
114728
- });
114729
114928
  }
114730
114929
  /**
114731
114930
  * A runner reached `running`: its episode is over. Close the runner alert so a
114732
- * later failure opens a NEW episode (new push key), instead of folding into a
114733
- * stale `refused` record that would never push again.
114931
+ * later failure opens a NEW episode, instead of folding into a stale
114932
+ * `refused` record whose notification was already sent.
114734
114933
  */
114735
114934
  async handleRunnerStateChanged(event) {
114736
114935
  if (event.data["state"] !== "running") return;
@@ -114756,24 +114955,20 @@ var require_alerts_addon = __commonJS({
114756
114955
  }) : [];
114757
114956
  const failedIds = ids(event.data["failed"]);
114758
114957
  const missingIds = ids(event.data["missing"]);
114759
- const pushLine = `${nodeId}: runners degraded - ${[
114958
+ const parts = [
114760
114959
  ...failedIds.length > 0 ? [`failed: ${failedIds.join(", ")}`] : [],
114761
114960
  ...refusedIds.length > 0 ? [`refused: ${refusedIds.join(", ")}`] : [],
114762
114961
  ...missingIds.length > 0 ? [`missing: ${missingIds.join(", ")}`] : []
114763
- ].join("; ")}. See the Addons page.`;
114764
- const pushKey = `check:${[
114765
- ...failedIds,
114766
- ...refusedIds,
114767
- ...missingIds
114768
- ].sort().join(",")}`;
114962
+ ];
114769
114963
  const now = Date.now();
114770
114964
  const existing = await this.readAlertRaw(alertId);
114771
114965
  const alert = {
114772
114966
  id: alertId,
114773
114967
  category: "system.runner-check",
114774
- severity: "error",
114968
+ severity: "critical",
114775
114969
  title: "Runners degraded",
114776
- message: `${nodeId}: ${summary}`,
114970
+ message: `${nodeId}: ${summary}${parts.length > 0 ? ` (${parts.join("; ")})` : ""}`,
114971
+ action: ADDONS_PAGE_ACTION,
114777
114972
  status: "active",
114778
114973
  read: false,
114779
114974
  createdAt: existing?.createdAt ?? now,
@@ -114786,19 +114981,11 @@ var require_alerts_addon = __commonJS({
114786
114981
  nodeId,
114787
114982
  failed: event.data["failed"],
114788
114983
  refused: event.data["refused"],
114789
- missing: event.data["missing"],
114790
- pushLine,
114791
- ...existing?.push !== void 0 ? { push: existing.push } : {}
114984
+ missing: event.data["missing"]
114792
114985
  }
114793
114986
  };
114794
114987
  await this.persistAlert(alert);
114795
114988
  this.emitAlertCreatedEvent(alert);
114796
- this.queuePush({
114797
- alertId,
114798
- key: pushKey,
114799
- title: alert.title,
114800
- line: pushLine
114801
- });
114802
114989
  }
114803
114990
  async readAlertRaw(alertId) {
114804
114991
  const store = this.alertStore;
@@ -114807,155 +114994,19 @@ var require_alerts_addon = __commonJS({
114807
114994
  if (raw === null) return null;
114808
114995
  const rec = Object.fromEntries(Object.entries(raw));
114809
114996
  const meta = rec["metadata"];
114810
- const push = typeof meta === "object" && meta !== null && !Array.isArray(meta) ? meta["push"] : void 0;
114811
114997
  const metadata = typeof meta === "object" && meta !== null && !Array.isArray(meta) ? meta : void 0;
114998
+ const parsed = require_dist10.AlertSchema.safeParse({
114999
+ id: alertId,
115000
+ ...rec
115001
+ });
114812
115002
  return {
114813
115003
  ...typeof rec["createdAt"] === "number" ? { createdAt: rec["createdAt"] } : {},
114814
115004
  ...typeof rec["read"] === "boolean" ? { read: rec["read"] } : {},
114815
115005
  ...typeof rec["status"] === "string" ? { status: rec["status"] } : {},
114816
- ...isPushState(push) ? { push } : {},
114817
- ...episodeKeyFromMetadata(metadata) !== void 0 ? { episodeKey: episodeKeyFromMetadata(metadata) } : {}
115006
+ ...episodeKeyFromMetadata(metadata) !== void 0 ? { episodeKey: episodeKeyFromMetadata(metadata) } : {},
115007
+ ...parsed.success ? { episode: episodeViewOf(parsed.data) } : {}
114818
115008
  };
114819
115009
  }
114820
- /** Pushes run one at a time so a burst can never send the same state twice. */
114821
- pushChain = Promise.resolve();
114822
- /** Findings waiting for the next digest, by alert id. */
114823
- pushQueue = /* @__PURE__ */ new Map();
114824
- pushTimer = null;
114825
- pushPolicy() {
114826
- return {
114827
- enabled: this.config.criticalPushEnabled,
114828
- targetIds: normalizeCriticalPushTargetIds(this.config.criticalPushTargetIds)
114829
- };
114830
- }
114831
- /** Rewrite a legacy CSV `criticalPushTargetIds` string into `string[]`. */
114832
- async migrateCriticalPushTargetIds() {
114833
- const raw = this.config.criticalPushTargetIds;
114834
- if (typeof raw !== "string") return;
114835
- try {
114836
- await this.updateGlobalSettings({ criticalPushTargetIds: normalizeCriticalPushTargetIds(raw) });
114837
- } catch (err) {
114838
- this.ctx.logger.warn("criticalPushTargetIds CSV migration failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
114839
- }
114840
- }
114841
- pushPorts() {
114842
- const api = this.ctx.api;
114843
- return {
114844
- listTargets: async () => {
114845
- return (await api.notificationOutput.listTargets.query({})).map((t) => {
114846
- const owner = t.config["ownerUserId"];
114847
- return {
114848
- id: t.id,
114849
- addonId: t.addonId,
114850
- enabled: t.enabled,
114851
- ...typeof owner === "string" && owner.length > 0 ? { ownerUserId: owner } : {}
114852
- };
114853
- });
114854
- },
114855
- send: async (target, message) => api.notificationOutput.send.mutate({
114856
- addonId: target.addonId,
114857
- targetId: target.id,
114858
- notification: {
114859
- title: message.title,
114860
- body: message.body,
114861
- format: "text",
114862
- priority: 5
114863
- }
114864
- })
114865
- };
114866
- }
114867
- /**
114868
- * Queue a critical finding for the next digest. A burst (every runner of a boot)
114869
- * arrives within the coalesce window and leaves as ONE message per target.
114870
- */
114871
- queuePush(item) {
114872
- this.pushQueue.set(item.alertId, item);
114873
- if (this.pushTimer !== null) return;
114874
- this.pushTimer = setTimeout(() => {
114875
- this.pushTimer = null;
114876
- this.flushPushes();
114877
- }, this.pushCoalesceMs);
114878
- this.pushTimer.unref?.();
114879
- }
114880
- /** Send the queued findings now (also the reconcile path). */
114881
- flushPushes() {
114882
- this.pushChain = this.pushChain.then(async () => {
114883
- const queued = [...this.pushQueue.values()];
114884
- this.pushQueue.clear();
114885
- if (queued.length === 0) return;
114886
- try {
114887
- const items = [];
114888
- for (const q of queued) {
114889
- const existing = await this.readAlertRaw(q.alertId);
114890
- items.push({
114891
- ...q,
114892
- previous: existing?.push ?? null
114893
- });
114894
- }
114895
- const states = await deliverDigest({
114896
- items,
114897
- policy: this.pushPolicy(),
114898
- ports: this.pushPorts(),
114899
- now: Date.now()
114900
- });
114901
- for (const [alertId, state] of states) {
114902
- await this.storePushState(alertId, state);
114903
- this.ctx.logger?.info("critical alert push", { meta: {
114904
- alertId,
114905
- state: state.state,
114906
- attempts: state.attempts,
114907
- error: state.lastError
114908
- } });
114909
- }
114910
- } catch (err) {
114911
- this.ctx.logger?.warn("critical alert push failed unexpectedly", { meta: { error: require_dist10.errMsg(err) } });
114912
- }
114913
- });
114914
- return this.pushChain;
114915
- }
114916
- async storePushState(alertId, push) {
114917
- const store = this.alertStore;
114918
- if (store === null) return;
114919
- const raw = await store.getData(alertId);
114920
- if (raw === null) return;
114921
- const meta = raw["metadata"];
114922
- const metadata = typeof meta === "object" && meta !== null && !Array.isArray(meta) ? meta : {};
114923
- await store.mergeUpdate(alertId, { metadata: {
114924
- ...metadata,
114925
- push
114926
- } });
114927
- }
114928
- /**
114929
- * Retry every critical alert whose push is still pending - at boot and when
114930
- * the notifiers or notification-center start. An alert whose state was already
114931
- * delivered is skipped by its key, so reconciling never sends twice.
114932
- */
114933
- async reconcilePendingPushes() {
114934
- const store = this.alertStore;
114935
- if (store === null) return;
114936
- try {
114937
- const rows = await store.listRecent(500);
114938
- for (const row of rows) {
114939
- const parsed = require_dist10.AlertSchema.safeParse({
114940
- id: row.id,
114941
- ...row.data
114942
- });
114943
- if (!parsed.success || parsed.data.status !== "active") continue;
114944
- if (!isCriticalPush(parsed.data)) continue;
114945
- const push = parsed.data.metadata?.["push"];
114946
- if (!isPushState(push) || push.state !== "pending") continue;
114947
- this.pushQueue.set(parsed.data.id, {
114948
- alertId: parsed.data.id,
114949
- key: push.key,
114950
- title: parsed.data.title,
114951
- line: String(parsed.data.metadata?.["pushLine"] ?? parsed.data.title)
114952
- });
114953
- }
114954
- await this.flushPushes();
114955
- } catch (err) {
114956
- this.ctx.logger?.warn("pending critical push reconcile failed", { meta: { error: require_dist10.errMsg(err) } });
114957
- }
114958
- }
114959
115010
  async handleModelDownload(event) {
114960
115011
  const data = event.data;
114961
115012
  const modelId = data.modelId ?? "unknown";
@@ -115302,7 +115353,7 @@ var require_alerts_addon = __commonJS({
115302
115353
  }
115303
115354
  async emitAlert(alert) {
115304
115355
  const existingRaw = await this.readAlertRaw(alert.id);
115305
- const { alert: toStore, queuePush } = mergeEmitAlert({
115356
+ const toStore = mergeEmitAlert({
115306
115357
  existing: existingRaw === null ? null : {
115307
115358
  read: existingRaw.read,
115308
115359
  episodeKey: existingRaw.episodeKey,
@@ -115310,20 +115361,10 @@ var require_alerts_addon = __commonJS({
115310
115361
  },
115311
115362
  incoming: alert
115312
115363
  });
115313
- const stored = alertWithRetainedPush(toStore, existingRaw?.push, existingRaw?.status);
115314
115364
  const isNew = existingRaw === null;
115315
- await this.persistAlert(stored);
115365
+ const stored = await this.persistAlert(toStore);
115316
115366
  if (isNew) this.emitAlertCreatedEvent(stored);
115317
115367
  else this.emitAlertUpdatedEvent(alert.id, stored);
115318
- if (queuePush) {
115319
- const { key, line } = criticalPushPayload(stored);
115320
- this.queuePush({
115321
- alertId: stored.id,
115322
- key,
115323
- title: stored.title,
115324
- line
115325
- });
115326
- }
115327
115368
  await this.enforceMaxAlerts();
115328
115369
  await this.enforceRetention();
115329
115370
  }
@@ -115338,15 +115379,73 @@ var require_alerts_addon = __commonJS({
115338
115379
  * Read-merge-write: SettingsBackend.update() replaces the entire `data` blob,
115339
115380
  * so we must read the existing record, merge the patch, and write back.
115340
115381
  */
115341
- async mergeUpdate(id, patch) {
115382
+ /** Alert writes (stamp + store + announce) from any caller, one at a time. */
115383
+ writeChain = Promise.resolve();
115384
+ serializedWrite(write) {
115385
+ const run = this.writeChain.then(write, write);
115386
+ this.writeChain = run.catch(() => void 0);
115387
+ return run;
115388
+ }
115389
+ mergeUpdate(id, patch) {
115390
+ return this.serializedWrite(() => this.mergeUpdateNow(id, patch));
115391
+ }
115392
+ async mergeUpdateNow(id, patch) {
115342
115393
  const store = this.alertStore;
115343
115394
  if (store === null) return;
115344
- await store.mergeUpdate(id, patch);
115395
+ const previous = (await this.readAlertRaw(id))?.episode ?? null;
115396
+ const metadata = patch["metadata"];
115397
+ const keepEpisode = previous !== null && typeof metadata === "object" && metadata !== null ? { metadata: {
115398
+ ...metadata,
115399
+ episodeId: previous.episodeId
115400
+ } } : {};
115401
+ await store.mergeUpdate(id, {
115402
+ ...patch,
115403
+ ...keepEpisode
115404
+ });
115405
+ const after = await store.getData(id);
115406
+ const parsed = after === null ? null : require_dist10.AlertSchema.safeParse({
115407
+ id,
115408
+ ...after
115409
+ });
115410
+ if (parsed?.success === true) this.announceEpisode(previous, parsed.data);
115411
+ }
115412
+ /**
115413
+ * Store an alert, stamping its episode identity. The stored alert is what a
115414
+ * caller must use afterwards, and the opening or closing of a critical/error
115415
+ * episode is announced here, in the one place every write passes through.
115416
+ */
115417
+ persistAlert(alert) {
115418
+ return this.serializedWrite(() => this.persistAlertNow(alert));
115345
115419
  }
115346
- async persistAlert(alert) {
115420
+ async persistAlertNow(alert) {
115347
115421
  const store = this.alertStore;
115348
- if (store === null) return;
115349
- await store.persist(alert);
115422
+ if (store === null) return alert;
115423
+ const previous = (await this.readAlertRaw(alert.id))?.episode ?? null;
115424
+ const episodeId = nextEpisodeId({
115425
+ alertId: alert.id,
115426
+ previous,
115427
+ incomingKey: episodeKeyFromMetadata(alert.metadata),
115428
+ now: Date.now()
115429
+ });
115430
+ const stamped = {
115431
+ ...alert,
115432
+ metadata: {
115433
+ ...alert.metadata,
115434
+ episodeId
115435
+ }
115436
+ };
115437
+ await store.persist(stamped);
115438
+ this.announceEpisode(previous, stamped);
115439
+ return stamped;
115440
+ }
115441
+ /**
115442
+ * Announce an episode edge on the bus. Telemetry, and it may be dropped
115443
+ * (D8): Notification Center reconciles from the alerts store (D11, D49).
115444
+ */
115445
+ announceEpisode(previous, alert) {
115446
+ const edge = episodeEdge(previous, episodeViewOf(alert));
115447
+ if (edge === null) return;
115448
+ this.ctx.eventBus?.emit(require_dist10.createEvent(require_dist10.EventCategory.AlertEpisode, EVENT_SOURCE, require_dist10.alertEpisodePayload(alert, edge, Date.now())));
115350
115449
  }
115351
115450
  emitAlertCreatedEvent(alert) {
115352
115451
  this.ctx.eventBus?.emit(require_dist10.createEvent("alert.created", EVENT_SOURCE, {
@@ -115447,7 +115546,7 @@ var require_calendars_addon = __commonJS({
115447
115546
  [Symbol.toStringTag]: { value: "Module" }
115448
115547
  });
115449
115548
  require_chunk_Cek0wNdY();
115450
- var require_dist10 = require_dist_Ct88catN();
115549
+ var require_dist10 = require_dist_Bi4Ioxop();
115451
115550
  var require_settle_sources = require_settle_sources_Bhsy57y();
115452
115551
  var zod = require_zod();
115453
115552
  var CALENDAR_COLOR_PALETTE = [
@@ -115665,11 +115764,11 @@ var require_calendars = __commonJS({
115665
115764
  }
115666
115765
  });
115667
115766
 
115668
- // ../system/dist/composition-sources-BQmhc3se.js
115669
- var require_composition_sources_BQmhc3se = __commonJS({
115670
- "../system/dist/composition-sources-BQmhc3se.js"(exports) {
115767
+ // ../system/dist/composition-sources-Cukc78eu.js
115768
+ var require_composition_sources_Cukc78eu = __commonJS({
115769
+ "../system/dist/composition-sources-Cukc78eu.js"(exports) {
115671
115770
  "use strict";
115672
- var require_dist10 = require_dist_Ct88catN();
115771
+ var require_dist10 = require_dist_Bi4Ioxop();
115673
115772
  function normalizedNativeOwner(owner) {
115674
115773
  const trimmed = owner?.trim();
115675
115774
  return trimmed ? trimmed : null;
@@ -115784,8 +115883,8 @@ var require_composer_addon = __commonJS({
115784
115883
  [Symbol.toStringTag]: { value: "Module" }
115785
115884
  });
115786
115885
  require_chunk_Cek0wNdY();
115787
- var require_dist10 = require_dist_Ct88catN();
115788
- var require_composition_sources = require_composition_sources_BQmhc3se();
115886
+ var require_dist10 = require_dist_Bi4Ioxop();
115887
+ var require_composition_sources = require_composition_sources_Cukc78eu();
115789
115888
  var zod = require_zod();
115790
115889
  function isRecord$2(value) {
115791
115890
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -119595,7 +119694,7 @@ var require_console_logging = __commonJS({
119595
119694
  [Symbol.toStringTag]: { value: "Module" }
119596
119695
  });
119597
119696
  require_chunk_Cek0wNdY();
119598
- var require_dist10 = require_dist_Ct88catN();
119697
+ var require_dist10 = require_dist_Bi4Ioxop();
119599
119698
  var require_formatter = require_formatter_DqAKDlvN();
119600
119699
  var LEVEL_RANK = {
119601
119700
  debug: 0,
@@ -123988,9 +124087,9 @@ var require_core_blocks_addon = __commonJS({
123988
124087
  "use strict";
123989
124088
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
123990
124089
  var require_chunk = require_chunk_Cek0wNdY();
123991
- var require_dist10 = require_dist_Ct88catN();
124090
+ var require_dist10 = require_dist_Bi4Ioxop();
123992
124091
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
123993
- var require_composition_sources = require_composition_sources_BQmhc3se();
124092
+ var require_composition_sources = require_composition_sources_Cukc78eu();
123994
124093
  var require_prefix_range = require_prefix_range_D_KueXbQ();
123995
124094
  var require_prefix_walk = require_prefix_walk_Dv6MLZlM();
123996
124095
  var zod = require_zod();
@@ -126190,9 +126289,9 @@ var require_core_blocks = __commonJS({
126190
126289
  }
126191
126290
  });
126192
126291
 
126193
- // ../types/dist/event-category-Hoe-mqbd.js
126194
- var require_event_category_Hoe_mqbd = __commonJS({
126195
- "../types/dist/event-category-Hoe-mqbd.js"(exports) {
126292
+ // ../types/dist/event-category-eSqkye-K.js
126293
+ var require_event_category_eSqkye_K = __commonJS({
126294
+ "../types/dist/event-category-eSqkye-K.js"(exports) {
126196
126295
  "use strict";
126197
126296
  var EventCategory = /* @__PURE__ */ (function(EventCategory2) {
126198
126297
  EventCategory2["SystemBoot"] = "system.boot";
@@ -126213,6 +126312,7 @@ var require_event_category_Hoe_mqbd = __commonJS({
126213
126312
  EventCategory2["AddonRunnerRefused"] = "addon.runner-refused";
126214
126313
  EventCategory2["ProcessStateChanged"] = "process.state-changed";
126215
126314
  EventCategory2["SystemRunnerCheck"] = "system.runner-check";
126315
+ EventCategory2["AlertEpisode"] = "alert.episode";
126216
126316
  EventCategory2["AddonError"] = "addon.error";
126217
126317
  EventCategory2["AddonPageReady"] = "addon.page-ready";
126218
126318
  EventCategory2["AddonWidgetReady"] = "addon.widget-ready";
@@ -126394,11 +126494,11 @@ var require_event_category_Hoe_mqbd = __commonJS({
126394
126494
  }
126395
126495
  });
126396
126496
 
126397
- // ../types/dist/sleep--L6qY125.js
126398
- var require_sleep_L6qY125 = __commonJS({
126399
- "../types/dist/sleep--L6qY125.js"(exports) {
126497
+ // ../types/dist/sleep-BE7_6UnC.js
126498
+ var require_sleep_BE7_6UnC = __commonJS({
126499
+ "../types/dist/sleep-BE7_6UnC.js"(exports) {
126400
126500
  "use strict";
126401
- var require_event_category = require_event_category_Hoe_mqbd();
126501
+ var require_event_category = require_event_category_eSqkye_K();
126402
126502
  var zod = require_zod();
126403
126503
  var AUDIO_CHUNK_FORMATS = [
126404
126504
  "f32le",
@@ -131460,14 +131560,19 @@ var require_sleep_L6qY125 = __commonJS({
131460
131560
  function addonSliceHandleApi(api) {
131461
131561
  return api;
131462
131562
  }
131563
+ function isCapRecord(value) {
131564
+ if (value === null || Array.isArray(value)) return false;
131565
+ const kind = typeof value;
131566
+ return kind === "object" || kind === "function";
131567
+ }
131463
131568
  function isTrpcProcedureLeaf(value) {
131464
- if (typeof value !== "object" || value === null) return false;
131569
+ if (!isCapRecord(value)) return false;
131465
131570
  const record = value;
131466
- return (typeof record.query === "function" || typeof record.mutate === "function" || typeof record.subscribe === "function") && !Array.isArray(value);
131571
+ return typeof record.query === "function" || typeof record.mutate === "function" || typeof record.subscribe === "function";
131467
131572
  }
131468
131573
  function readCapRecord(api, capProp) {
131469
131574
  const cap = Reflect.get(api, capProp);
131470
- if (typeof cap !== "object" || cap === null || Array.isArray(cap)) return void 0;
131575
+ if (!isCapRecord(cap)) return void 0;
131471
131576
  return cap;
131472
131577
  }
131473
131578
  function readTrpcLeaf(api, capProp, method2) {
@@ -131647,12 +131752,18 @@ var require_sleep_L6qY125 = __commonJS({
131647
131752
  const dishwasherInterface = {
131648
131753
  start: (input) => dispatch("dishwasher", "dishwasher", "start", "mutation", input),
131649
131754
  pause: (input) => dispatch("dishwasher", "dishwasher", "pause", "mutation", input),
131755
+ resume: (input) => dispatch("dishwasher", "dishwasher", "resume", "mutation", input),
131650
131756
  stop: (input) => dispatch("dishwasher", "dishwasher", "stop", "mutation", input),
131651
131757
  setProgram: (input) => dispatch("dishwasher", "dishwasher", "setProgram", "mutation", input),
131652
131758
  setPower: (input) => dispatch("dishwasher", "dishwasher", "setPower", "mutation", input),
131653
131759
  setHalfLoad: (input) => dispatch("dishwasher", "dishwasher", "setHalfLoad", "mutation", input),
131654
131760
  setHygienePlus: (input) => dispatch("dishwasher", "dishwasher", "setHygienePlus", "mutation", input),
131761
+ setExtraDry: (input) => dispatch("dishwasher", "dishwasher", "setExtraDry", "mutation", input),
131762
+ setIntensiveZone: (input) => dispatch("dishwasher", "dishwasher", "setIntensiveZone", "mutation", input),
131763
+ setVarioSpeedPlus: (input) => dispatch("dishwasher", "dishwasher", "setVarioSpeedPlus", "mutation", input),
131764
+ setChildLock: (input) => dispatch("dishwasher", "dishwasher", "setChildLock", "mutation", input),
131655
131765
  setOption: (input) => dispatch("dishwasher", "dishwasher", "setOption", "mutation", input),
131766
+ getOptions: (input) => dispatch("dishwasher", "dishwasher", "getOptions", "query", input),
131656
131767
  getStatus: (input) => dispatch("dishwasher", "dishwasher", "getStatus", "query", input)
131657
131768
  };
131658
131769
  const doorbellInterface = { getStatus: (input) => dispatch("doorbell", "doorbell", "getStatus", "query", input) };
@@ -133950,8 +134061,8 @@ var require_addon = __commonJS({
133950
134061
  "../types/dist/addon.js"(exports) {
133951
134062
  "use strict";
133952
134063
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
133953
- var require_sleep = require_sleep_L6qY125();
133954
- var require_event_category = require_event_category_Hoe_mqbd();
134064
+ var require_sleep = require_sleep_BE7_6UnC();
134065
+ var require_event_category = require_event_category_eSqkye_K();
133955
134066
  var require_err_msg = require_err_msg_BhVbr0NX();
133956
134067
  var CAP_INPUT_DEFAULTS = Object.freeze({
133957
134068
  "addons": { "getLogs": { "limit": 100 } },
@@ -134855,6 +134966,15 @@ var require_addon = __commonJS({
134855
134966
  "valueType": "boolean"
134856
134967
  }],
134857
134968
  "dishwasher": [
134969
+ {
134970
+ "path": "availability",
134971
+ "valueType": "enum",
134972
+ "enumValues": [
134973
+ "available",
134974
+ "offline",
134975
+ "description-missing"
134976
+ ]
134977
+ },
134858
134978
  {
134859
134979
  "path": "state",
134860
134980
  "valueType": "enum",
@@ -134867,12 +134987,35 @@ var require_addon = __commonJS({
134867
134987
  "unavailable"
134868
134988
  ]
134869
134989
  },
134990
+ {
134991
+ "path": "operationState",
134992
+ "valueType": "enum",
134993
+ "enumValues": [
134994
+ "Inactive",
134995
+ "Ready",
134996
+ "DelayedStart",
134997
+ "Run",
134998
+ "Pause",
134999
+ "ActionRequired",
135000
+ "Finished",
135001
+ "Error",
135002
+ "Aborting"
135003
+ ]
135004
+ },
134870
135005
  {
134871
135006
  "path": "program",
134872
135007
  "valueType": "string"
134873
135008
  },
134874
135009
  {
134875
- "path": "remainingMinutes",
135010
+ "path": "selectedProgram",
135011
+ "valueType": "string"
135012
+ },
135013
+ {
135014
+ "path": "remainingSeconds",
135015
+ "valueType": "number"
135016
+ },
135017
+ {
135018
+ "path": "delayedStartSeconds",
134876
135019
  "valueType": "number"
134877
135020
  },
134878
135021
  {
@@ -134883,14 +135026,30 @@ var require_addon = __commonJS({
134883
135026
  "path": "doorOpen",
134884
135027
  "valueType": "boolean"
134885
135028
  },
135029
+ {
135030
+ "path": "doorLocked",
135031
+ "valueType": "boolean"
135032
+ },
134886
135033
  {
134887
135034
  "path": "remoteStartAllowed",
134888
135035
  "valueType": "boolean"
134889
135036
  },
135037
+ {
135038
+ "path": "remoteControlActive",
135039
+ "valueType": "boolean"
135040
+ },
135041
+ {
135042
+ "path": "localControlActive",
135043
+ "valueType": "boolean"
135044
+ },
134890
135045
  {
134891
135046
  "path": "powerOn",
134892
135047
  "valueType": "boolean"
134893
135048
  },
135049
+ {
135050
+ "path": "powerState",
135051
+ "valueType": "string"
135052
+ },
134894
135053
  {
134895
135054
  "path": "saltLevel",
134896
135055
  "valueType": "enum",
@@ -134917,6 +135076,22 @@ var require_addon = __commonJS({
134917
135076
  "path": "hygienePlus",
134918
135077
  "valueType": "boolean"
134919
135078
  },
135079
+ {
135080
+ "path": "extraDry",
135081
+ "valueType": "boolean"
135082
+ },
135083
+ {
135084
+ "path": "intensiveZone",
135085
+ "valueType": "boolean"
135086
+ },
135087
+ {
135088
+ "path": "varioSpeedPlus",
135089
+ "valueType": "boolean"
135090
+ },
135091
+ {
135092
+ "path": "childLock",
135093
+ "valueType": "boolean"
135094
+ },
134920
135095
  {
134921
135096
  "path": "errorCode",
134922
135097
  "valueType": "number"
@@ -136532,7 +136707,7 @@ var require_device_manager_addon = __commonJS({
136532
136707
  [Symbol.toStringTag]: { value: "Module" }
136533
136708
  });
136534
136709
  require_chunk_Cek0wNdY();
136535
- var require_dist10 = require_dist_Ct88catN();
136710
+ var require_dist10 = require_dist_Bi4Ioxop();
136536
136711
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
136537
136712
  var require_prefix_range = require_prefix_range_D_KueXbQ();
136538
136713
  var require_prefix_walk = require_prefix_walk_Dv6MLZlM();
@@ -145606,7 +145781,7 @@ var require_hub_forwarder = __commonJS({
145606
145781
  [Symbol.toStringTag]: { value: "Module" }
145607
145782
  });
145608
145783
  require_chunk_Cek0wNdY();
145609
- var require_dist10 = require_dist_Ct88catN();
145784
+ var require_dist10 = require_dist_Bi4Ioxop();
145610
145785
  var require_formatter = require_formatter_DqAKDlvN();
145611
145786
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
145612
145787
  var HubForwarderDestination = class {
@@ -145743,7 +145918,7 @@ var require_liveness_monitor_addon = __commonJS({
145743
145918
  "use strict";
145744
145919
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
145745
145920
  require_chunk_Cek0wNdY();
145746
- var require_dist10 = require_dist_Ct88catN();
145921
+ var require_dist10 = require_dist_Bi4Ioxop();
145747
145922
  var NO_DEVICES = "liveness:no-devices";
145748
145923
  var ALL_OFFLINE = "liveness:all-devices-offline";
145749
145924
  var NO_FOOTAGE_PREFIX = "liveness:no-footage:";
@@ -146106,12 +146281,12 @@ var require_local_auth_addon = __commonJS({
146106
146281
  [Symbol.toStringTag]: { value: "Module" }
146107
146282
  });
146108
146283
  var require_chunk = require_chunk_Cek0wNdY();
146109
- var require_dist10 = require_dist_Ct88catN();
146284
+ var require_dist10 = require_dist_Bi4Ioxop();
146110
146285
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
146111
146286
  var require_safe_buffer$1 = require_safe_buffer_D3aoa_7X();
146112
146287
  var require_ms$1 = require_ms_LB3giGoU();
146113
- var require_device_ca = require_device_ca_75rt3U_U();
146114
- var require_phase6_declare_gate = require_phase6_declare_gate_C6WqagkC();
146288
+ var require_device_ca = require_device_ca_B3hUVfYk();
146289
+ var require_phase6_declare_gate = require_phase6_declare_gate_DwYAep7y();
146115
146290
  var zod = require_zod();
146116
146291
  var node_crypto = __require("crypto");
146117
146292
  node_crypto = require_chunk.__toESM(node_crypto);
@@ -156906,7 +157081,7 @@ var require_loki_logging = __commonJS({
156906
157081
  [Symbol.toStringTag]: { value: "Module" }
156907
157082
  });
156908
157083
  require_chunk_Cek0wNdY();
156909
- var require_dist10 = require_dist_Ct88catN();
157084
+ var require_dist10 = require_dist_Bi4Ioxop();
156910
157085
  function sanitizeLabelName(raw) {
156911
157086
  const replaced = raw.replaceAll(/[^a-zA-Z0-9_]/g, "_");
156912
157087
  return /^[a-zA-Z_]/.test(replaced) ? replaced : `_${replaced}`;
@@ -157578,7 +157753,7 @@ var require_native_metrics_addon = __commonJS({
157578
157753
  [Symbol.toStringTag]: { value: "Module" }
157579
157754
  });
157580
157755
  var require_chunk = require_chunk_Cek0wNdY();
157581
- var require_dist10 = require_dist_Ct88catN();
157756
+ var require_dist10 = require_dist_Bi4Ioxop();
157582
157757
  var node_fs_promises = __require("fs/promises");
157583
157758
  var node_util = __require("util");
157584
157759
  var node_os = __require("os");
@@ -158365,6 +158540,11 @@ var require_native_metrics_addon = __commonJS({
158365
158540
  }
158366
158541
  };
158367
158542
  }
158543
+ function refusalCounts(result) {
158544
+ const counts = {};
158545
+ for (const r of result.refused) counts[r.reason] = (counts[r.reason] ?? 0) + r.count;
158546
+ return counts;
158547
+ }
158368
158548
  var NodeMetricsHistoryRecorder = class {
158369
158549
  deps;
158370
158550
  declared = false;
@@ -158438,7 +158618,15 @@ var require_native_metrics_addon = __commonJS({
158438
158618
  });
158439
158619
  if (points.length === 0) return;
158440
158620
  try {
158441
- await this.deps.deviceHistory.record({ points });
158621
+ const result = await this.deps.deviceHistory.record({ points });
158622
+ if (result.refused.length > 0) {
158623
+ if (result.refused.some((r) => r.reason === "undeclared")) this.declared = false;
158624
+ this.deps.logger.warn("node metrics history points refused", { meta: {
158625
+ nodeId,
158626
+ accepted: result.accepted,
158627
+ refused: refusalCounts(result)
158628
+ } });
158629
+ }
158442
158630
  } catch (err) {
158443
158631
  this.deps.logger.warn("node metrics history sample not stored", { meta: {
158444
158632
  nodeId,
@@ -159959,7 +160147,7 @@ var require_device_history_addon = __commonJS({
159959
160147
  [Symbol.toStringTag]: { value: "Module" }
159960
160148
  });
159961
160149
  require_chunk_Cek0wNdY();
159962
- var require_dist10 = require_dist_Ct88catN();
160150
+ var require_dist10 = require_dist_Bi4Ioxop();
159963
160151
  function subjectIdOf(subject) {
159964
160152
  return subject.kind === "device" ? `d:${subject.deviceId}` : `n:${subject.nodeId}`;
159965
160153
  }
@@ -162638,7 +162826,7 @@ var require_filesystem_storage_addon = __commonJS({
162638
162826
  [Symbol.toStringTag]: { value: "Module" }
162639
162827
  });
162640
162828
  var require_chunk = require_chunk_Cek0wNdY();
162641
- var require_dist10 = require_dist_Ct88catN();
162829
+ var require_dist10 = require_dist_Bi4Ioxop();
162642
162830
  var node_crypto = __require("crypto");
162643
162831
  var node_fs_promises = __require("fs/promises");
162644
162832
  var node_path = __require("path");
@@ -163748,12 +163936,12 @@ var require_lib = __commonJS({
163748
163936
  }
163749
163937
  });
163750
163938
 
163751
- // ../system/dist/wal-maintenance-mwCBe3Vr.js
163752
- var require_wal_maintenance_mwCBe3Vr = __commonJS({
163753
- "../system/dist/wal-maintenance-mwCBe3Vr.js"(exports) {
163939
+ // ../system/dist/wal-maintenance-Bo_lo3Dh.js
163940
+ var require_wal_maintenance_Bo_lo3Dh = __commonJS({
163941
+ "../system/dist/wal-maintenance-Bo_lo3Dh.js"(exports) {
163754
163942
  "use strict";
163755
163943
  var require_chunk = require_chunk_Cek0wNdY();
163756
- var require_dist10 = require_dist_Ct88catN();
163944
+ var require_dist10 = require_dist_Bi4Ioxop();
163757
163945
  var require_prefix_range = require_prefix_range_D_KueXbQ();
163758
163946
  var node_crypto = __require("crypto");
163759
163947
  var node_module = __require("module");
@@ -164038,7 +164226,9 @@ var require_wal_maintenance_mwCBe3Vr = __commonJS({
164038
164226
  return {
164039
164227
  hasTable: ({ name }) => backend.hasTable(name),
164040
164228
  dropRetiredTable: ({ name }) => backend.dropRetiredTable(name),
164041
- threadId: () => threadId
164229
+ threadId: () => threadId,
164230
+ listPhysicalTables: ({ ownerPrefix }) => backend.listTables(ownerPrefix),
164231
+ readLegacyCollection: (input) => backend.readLegacyCollection(input)
164042
164232
  };
164043
164233
  }
164044
164234
  function callControl(target, op, input) {
@@ -164689,8 +164879,41 @@ CREATE TABLE IF NOT EXISTS _record_sequences (
164689
164879
  if (this.schemaReady) {
164690
164880
  ensureRecordsSchema(db);
164691
164881
  for (const row of loadKinds(db)) this.kinds.set(keyOf(row.owner, row.kind), row);
164882
+ this.correctSequences();
164692
164883
  }
164693
164884
  }
164885
+ /**
164886
+ * Boot correction: every kind that ever allocated an id has its counter raised
164887
+ * above the highest numeric id it holds. Adopted legacy rows (or any caller
164888
+ * chosen numeric id) kept ids far above a counter that started at 1, and the
164889
+ * upsert would have overwritten another device's row on the first collision.
164890
+ * Runs on the engine's own connection, over the (kind_id, id) unique index.
164891
+ */
164892
+ correctSequences() {
164893
+ const seq = this.db.prepare("SELECT kind_id FROM _record_sequences");
164894
+ for (const { kind_id: kindId } of seq.all()) {
164895
+ const top = this.db.prepare("SELECT MAX(CAST(id AS INTEGER)) AS m FROM records WHERE kind_id = ? AND id GLOB '[0-9]*'").get(kindId);
164896
+ if (top?.m === null || top === void 0) continue;
164897
+ if (this.db.prepare("UPDATE _record_sequences SET next = ? WHERE kind_id = ? AND next < ?").run(top.m + 1, kindId, top.m + 1).changes > 0) this.options.logger.warn("records: id sequence raised above the highest stored id", { meta: {
164898
+ kindId,
164899
+ next: top.m + 1
164900
+ } });
164901
+ }
164902
+ }
164903
+ /** Ids handed out by allocateId and not yet written: their first write is insert-only. */
164904
+ pendingAllocated = /* @__PURE__ */ new Set();
164905
+ static PENDING_ALLOCATED_MAX = 4096;
164906
+ /** Raise a kind's counter above the numeric ids of a caller-supplied batch (adoption). */
164907
+ raiseSequenceFor(k, records) {
164908
+ let top = -1;
164909
+ for (const r of records) if (/^\d{1,15}$/.test(r.id)) top = Math.max(top, Number(r.id));
164910
+ if (top < 0) return;
164911
+ if (k.declaration.adoptsFrom !== void 0) {
164912
+ this.db.prepare("INSERT INTO _record_sequences(kind_id, next) VALUES (?, ?) ON CONFLICT(kind_id) DO UPDATE SET next = MAX(next, excluded.next)").run(k.kindId, top + 1);
164913
+ return;
164914
+ }
164915
+ this.db.prepare("UPDATE _record_sequences SET next = ? WHERE kind_id = ? AND next < ?").run(top + 1, k.kindId, top + 1);
164916
+ }
164694
164917
  schemaReady;
164695
164918
  kindOf(owner, kind) {
164696
164919
  const row = this.kinds.get(keyOf(owner, kind));
@@ -164829,6 +165052,17 @@ CREATE TABLE IF NOT EXISTS _record_sequences (
164829
165052
  /** One row, inside the caller's transaction. */
164830
165053
  writeRecord(owner, k, rec, ifRev, nowMs) {
164831
165054
  const header = validateHeader(k.kind, k.declaration, rec, nowMs);
165055
+ const allocatedKey = `${k.kindId}:${rec.id}`;
165056
+ if (this.pendingAllocated.has(allocatedKey) && this.storedRow(k, rec.id) !== void 0) {
165057
+ this.options.logger.warn("records: an allocated id collided with a stored row; write refused", {
165058
+ tags: { deviceId: rec.deviceId },
165059
+ meta: {
165060
+ kind: k.kind,
165061
+ id: rec.id
165062
+ }
165063
+ });
165064
+ throw new require_dist10.RevConflictError(`${k.kind}/${rec.id}: an allocated id is insert-only and a row already holds it`);
165065
+ }
164832
165066
  const stored = k.declaration.secretPaths.length > 0 || ifRev !== void 0 ? this.storedRow(k, rec.id) : void 0;
164833
165067
  if (ifRev !== void 0) {
164834
165068
  if (ifRev === 0 && stored !== void 0) throw new require_dist10.RevConflictError(`${k.kind}/${rec.id}: the row exists (ifRev 0 means "must not exist")`);
@@ -164842,6 +165076,7 @@ CREATE TABLE IF NOT EXISTS _record_sequences (
164842
165076
  } }), nowMs);
164843
165077
  const row = this.wrapWrite(k, () => this.db.prepare(this.upsertSql(k.kindId)).get(header.id, header.deviceId, header.at, header.expiresAt, nowMs, json));
164844
165078
  if (row === void 0) throw new Error("records: the upsert returned no revision");
165079
+ this.pendingAllocated.delete(allocatedKey);
164845
165080
  return {
164846
165081
  rev: row.rev,
164847
165082
  releasedRefs: checked.releasedRefs
@@ -164866,6 +165101,7 @@ CREATE TABLE IF NOT EXISTS _record_sequences (
164866
165101
  const released = [];
164867
165102
  this.timed("putMany", input.kind, () => this.db.transaction(() => {
164868
165103
  for (const rec of input.records) released.push(...this.writeRecord(input.owner, k, rec, void 0, nowMs).releasedRefs);
165104
+ this.raiseSequenceFor(k, input.records);
164869
165105
  })());
164870
165106
  this.afterWrite(k, input.records.map((r) => r.deviceId));
164871
165107
  return {
@@ -164988,6 +165224,11 @@ CREATE TABLE IF NOT EXISTS _record_sequences (
164988
165224
  const k = this.kindOf(input.owner, input.kind);
164989
165225
  const row = this.db.prepare("INSERT INTO _record_sequences(kind_id, next) VALUES (?, 2) ON CONFLICT(kind_id) DO UPDATE SET next = next + 1 RETURNING next - 1 AS id").get(k.kindId);
164990
165226
  if (row === void 0) throw new Error("records: the sequence returned no id");
165227
+ this.pendingAllocated.add(`${k.kindId}:${row.id}`);
165228
+ if (this.pendingAllocated.size > RecordsEngine2.PENDING_ALLOCATED_MAX) {
165229
+ const oldest = this.pendingAllocated.values().next();
165230
+ if (oldest.done !== true) this.pendingAllocated.delete(oldest.value);
165231
+ }
164991
165232
  return { id: row.id };
164992
165233
  }
164993
165234
  /**
@@ -165155,7 +165396,9 @@ CREATE TABLE IF NOT EXISTS _record_sequences (
165155
165396
  var CONTROL_OPS = [
165156
165397
  "hasTable",
165157
165398
  "dropRetiredTable",
165158
- "threadId"
165399
+ "threadId",
165400
+ "listPhysicalTables",
165401
+ "readLegacyCollection"
165159
165402
  ];
165160
165403
  function isDataStoreOp(op) {
165161
165404
  return DATA_STORE_OPS.some((known) => known === op);
@@ -166332,13 +166575,24 @@ CREATE TABLE IF NOT EXISTS _record_sequences (
166332
166575
  */
166333
166576
  listTables(ownerPrefix) {
166334
166577
  const db = this.getDb();
166335
- const names = db.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name").all().map((r) => r.name);
166578
+ const entries = db.prepare("SELECT name, sql FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name").all();
166336
166579
  const out = [];
166337
- for (const name of names) {
166580
+ for (const { name, sql } of entries) {
166581
+ if (sql !== null && sql.toUpperCase().startsWith("CREATE VIRTUAL")) continue;
166338
166582
  const colon = name.indexOf(":");
166339
166583
  const owner = colon > 0 ? name.slice(0, colon) : null;
166340
166584
  if (ownerPrefix !== void 0 && owner !== ownerPrefix) continue;
166341
- const rows = db.prepare(`SELECT COUNT(*) AS c FROM "${name}"`).get()?.c ?? 0;
166585
+ let rows = 0;
166586
+ try {
166587
+ rows = db.prepare(`SELECT COUNT(*) AS c FROM "${name}"`).get()?.c ?? 0;
166588
+ } catch (err) {
166589
+ this.logger?.warn("listTables: skipped a table whose COUNT failed", { meta: {
166590
+ table: name,
166591
+ database: this.options.databaseId,
166592
+ error: err instanceof Error ? err.message : String(err)
166593
+ } });
166594
+ continue;
166595
+ }
166342
166596
  let bytes = null;
166343
166597
  try {
166344
166598
  bytes = db.prepare("SELECT SUM(pgsize) AS b FROM dbstat WHERE name = ?").get(name)?.b ?? null;
@@ -167937,9 +168191,9 @@ var require_sqlite_settings_addon = __commonJS({
167937
168191
  [Symbol.toStringTag]: { value: "Module" }
167938
168192
  });
167939
168193
  var require_chunk = require_chunk_Cek0wNdY();
167940
- var require_dist10 = require_dist_Ct88catN();
168194
+ var require_dist10 = require_dist_Bi4Ioxop();
167941
168195
  var require_retired_settings_keys = require_retired_settings_keys_D_VwZiDt();
167942
- var require_wal_maintenance = require_wal_maintenance_mwCBe3Vr();
168196
+ var require_wal_maintenance = require_wal_maintenance_Bo_lo3Dh();
167943
168197
  var require_sqlite_snapshot_protocol = require_sqlite_snapshot_protocol_B9OndqZv();
167944
168198
  var require_wal_checkpoint_policy = require_wal_checkpoint_policy_d2z2qPqx();
167945
168199
  var node_fs_promises = __require("fs/promises");
@@ -168822,6 +169076,12 @@ var require_sqlite_settings_addon = __commonJS({
168822
169076
  async hasTable(name) {
168823
169077
  return require_wal_maintenance.callControl(require_wal_maintenance.controlTargetOf(this.backend, 0), "hasTable", { name });
168824
169078
  }
169079
+ listPhysicalTables(ownerPrefix) {
169080
+ return Promise.resolve(require_wal_maintenance.callControl(require_wal_maintenance.controlTargetOf(this.backend, 0), "listPhysicalTables", { ownerPrefix }));
169081
+ }
169082
+ readLegacyCollection(input) {
169083
+ return Promise.resolve(require_wal_maintenance.callControl(require_wal_maintenance.controlTargetOf(this.backend, 0), "readLegacyCollection", input));
169084
+ }
168825
169085
  async dropRetiredTable(name) {
168826
169086
  return require_wal_maintenance.callControl(require_wal_maintenance.controlTargetOf(this.backend, 0), "dropRetiredTable", { name });
168827
169087
  }
@@ -168875,6 +169135,12 @@ var require_sqlite_settings_addon = __commonJS({
168875
169135
  hasTable(name) {
168876
169136
  return this.client.control("hasTable", { name });
168877
169137
  }
169138
+ listPhysicalTables(ownerPrefix) {
169139
+ return this.client.control("listPhysicalTables", { ownerPrefix });
169140
+ }
169141
+ readLegacyCollection(input) {
169142
+ return this.client.control("readLegacyCollection", input);
169143
+ }
168878
169144
  dropRetiredTable(name) {
168879
169145
  return this.client.control("dropRetiredTable", { name });
168880
169146
  }
@@ -169449,11 +169715,13 @@ var require_sqlite_settings_addon = __commonJS({
169449
169715
  logger;
169450
169716
  installedOwners;
169451
169717
  records;
169452
- constructor(backend, logger, installedOwners, records) {
169718
+ peerFiles;
169719
+ constructor(backend, logger, installedOwners, records, peerFiles = async () => []) {
169453
169720
  this.backend = backend;
169454
169721
  this.logger = logger;
169455
169722
  this.installedOwners = installedOwners;
169456
169723
  this.records = records;
169724
+ this.peerFiles = peerFiles;
169457
169725
  }
169458
169726
  listKinds(input) {
169459
169727
  return this.records.listKinds(input);
@@ -169467,8 +169735,25 @@ var require_sqlite_settings_addon = __commonJS({
169467
169735
  isAdoptedLegacyTable(input) {
169468
169736
  return this.records.isAdoptedLegacyTable(input.collection).then((adopted) => ({ adopted }));
169469
169737
  }
169470
- readLegacyCollection(input) {
169471
- return Promise.resolve(this.backend.readLegacyCollection(input));
169738
+ async readLegacyCollection(input) {
169739
+ if (this.backend.hasTable(input.collection)) {
169740
+ const page = this.backend.readLegacyCollection(input);
169741
+ return {
169742
+ rows: [...page.rows],
169743
+ nextCursor: page.nextCursor
169744
+ };
169745
+ }
169746
+ for (const file of await this.peerFiles()) if (await file.hasTable(input.collection)) {
169747
+ const page = await file.readLegacyCollection(input);
169748
+ return {
169749
+ rows: [...page.rows],
169750
+ nextCursor: page.nextCursor
169751
+ };
169752
+ }
169753
+ return {
169754
+ rows: [],
169755
+ nextCursor: null
169756
+ };
169472
169757
  }
169473
169758
  /** May this table be dropped AT ALL? Throws with the reason when not. */
169474
169759
  async assertDroppable(collection) {
@@ -169484,12 +169769,14 @@ var require_sqlite_settings_addon = __commonJS({
169484
169769
  if (installed.has(owner) && !await this.records.isAdoptedLegacyTable(collection)) throw new Error(`drop refused: "${collection}" belongs to the installed addon "${owner}"`);
169485
169770
  }
169486
169771
  async listTables(input) {
169487
- const tables = this.backend.listTables(input.ownerPrefix);
169772
+ const tables = [...this.backend.listTables(input.ownerPrefix)];
169773
+ for (const file of await this.peerFiles()) tables.push(...await file.listPhysicalTables(input.ownerPrefix));
169774
+ tables.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
169488
169775
  this.logger.info("storage-admin: listed tables", { meta: {
169489
169776
  tables: tables.length,
169490
169777
  ownerPrefix: input.ownerPrefix ?? null
169491
169778
  } });
169492
- return { tables: [...tables] };
169779
+ return { tables };
169493
169780
  }
169494
169781
  async dropCollection(input) {
169495
169782
  try {
@@ -169673,7 +169960,7 @@ var require_sqlite_settings_addon = __commonJS({
169673
169960
  },
169674
169961
  {
169675
169962
  capability: require_dist10.storageAdminCapability,
169676
- provider: new StorageAdminProvider(settings.backend, this.ctx.logger.child("StorageAdmin"), async () => new Set((await this.ctx.api.addons.list.query()).map((addon) => addon.manifest.id)), records)
169963
+ provider: new StorageAdminProvider(settings.backend, this.ctx.logger.child("StorageAdmin"), async () => new Set((await this.ctx.api.addons.list.query()).map((addon) => addon.manifest.id)), records, async () => router.openFiles().filter((file) => file.databaseId !== "settings"))
169677
169964
  }
169678
169965
  ];
169679
169966
  }
@@ -169763,7 +170050,7 @@ var require_sqlite_storage = __commonJS({
169763
170050
  });
169764
170051
  require_chunk_Cek0wNdY();
169765
170052
  var require_builtins_sqlite_storage_filesystem_storage_addon = require_filesystem_storage_addon();
169766
- var require_wal_maintenance = require_wal_maintenance_mwCBe3Vr();
170053
+ var require_wal_maintenance = require_wal_maintenance_Bo_lo3Dh();
169767
170054
  var require_builtins_sqlite_storage_sqlite_settings_addon = require_sqlite_settings_addon();
169768
170055
  var ConfigStore = class {
169769
170056
  db;
@@ -169847,7 +170134,7 @@ var require_storage_orchestrator_addon = __commonJS({
169847
170134
  [Symbol.toStringTag]: { value: "Module" }
169848
170135
  });
169849
170136
  var require_chunk = require_chunk_Cek0wNdY();
169850
- var require_dist10 = require_dist_Ct88catN();
170137
+ var require_dist10 = require_dist_Bi4Ioxop();
169851
170138
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
169852
170139
  var zod = require_zod();
169853
170140
  var node_crypto = __require("crypto");
@@ -169871,6 +170158,7 @@ var require_storage_orchestrator_addon = __commonJS({
169871
170158
  mode: require_dist10.StorageLocationModeSchema.optional(),
169872
170159
  isDefault: zod.z.boolean().optional()
169873
170160
  });
170161
+ var StorageSeedMarkerBodySchema = zod.z.object({ seededAt: zod.z.number() });
169874
170162
  var StorageDrainStateBodySchema = zod.z.object({
169875
170163
  locationId: zod.z.string(),
169876
170164
  startedAtMs: zod.z.number(),
@@ -169895,6 +170183,14 @@ var require_storage_orchestrator_addon = __commonJS({
169895
170183
  retention: { none: "operator delete or declaration prune only" },
169896
170184
  adoptsFrom: STORAGE_LOCATIONS
169897
170185
  },
170186
+ "seed-markers": {
170187
+ schema: StorageSeedMarkerBodySchema,
170188
+ class: "config",
170189
+ writeTrigger: "a declared location type was seeded (or found already present on an existing install); the id is the type",
170190
+ device: "none",
170191
+ at: "none",
170192
+ retention: { none: "one row per location type ever seeded \u2014 what makes an operator delete stick" }
170193
+ },
169898
170194
  "drain-state": {
169899
170195
  schema: StorageDrainStateBodySchema,
169900
170196
  class: "config",
@@ -169974,9 +170270,27 @@ var require_storage_orchestrator_addon = __commonJS({
169974
170270
  store;
169975
170271
  declared = false;
169976
170272
  locationsKind;
169977
- constructor(store, locationsKind = null) {
170273
+ constructor(store, locationsKind = null, seedMarkersKind = null) {
169978
170274
  this.store = store;
169979
170275
  this.locationsKind = locationsKind;
170276
+ this.seedMarkersKind = seedMarkersKind;
170277
+ }
170278
+ seedMarkersKind;
170279
+ static seedMarkersOf(records) {
170280
+ return records.kind(storageOrchestratorRecordKinds["seed-markers"]);
170281
+ }
170282
+ async loadSeededTypes() {
170283
+ const kind = this.seedMarkersKind;
170284
+ if (kind === null) return [];
170285
+ return (await (0, import_read_all_rows.readAllRows)(kind)).map((r) => r.id);
170286
+ }
170287
+ async markTypeSeeded(type) {
170288
+ const kind = this.seedMarkersKind;
170289
+ if (kind === null) return;
170290
+ await kind.put({
170291
+ id: type,
170292
+ data: { seededAt: Date.now() }
170293
+ });
169980
170294
  }
169981
170295
  static recordStoresOf(records) {
169982
170296
  return records.kind(storageOrchestratorRecordKinds.locations);
@@ -170943,20 +171257,77 @@ var require_storage_orchestrator_addon = __commonJS({
170943
171257
  }
170944
171258
  };
170945
171259
  }
170946
- async function collectFiles(io, prefix) {
171260
+ var STORAGE_CLASS_SUBTREES = {
171261
+ eventMedia: ["events"],
171262
+ recordingsLow: ["low"],
171263
+ recordings: ["high", "mid"]
171264
+ };
171265
+ function isDotEntry(name) {
171266
+ return name.startsWith(".");
171267
+ }
171268
+ async function dirHasAnyEntry(dir) {
171269
+ let handle;
171270
+ try {
171271
+ handle = await (0, node_fs_promises.opendir)(dir);
171272
+ } catch (err) {
171273
+ if (err.code === "ENOENT") return false;
171274
+ throw err;
171275
+ }
171276
+ try {
171277
+ return await handle.read() !== null;
171278
+ } finally {
171279
+ await handle.close();
171280
+ }
171281
+ }
171282
+ async function probeLocalDirectoryOccupancy(input) {
171283
+ let names;
171284
+ try {
171285
+ names = await (0, node_fs_promises.readdir)(input.basePath);
171286
+ } catch (err) {
171287
+ if (err.code === "ENOENT") return "empty";
171288
+ return "unknown";
171289
+ }
171290
+ const visible = names.filter((name) => !isDotEntry(name));
171291
+ const subtrees = STORAGE_CLASS_SUBTREES[input.type];
171292
+ if (subtrees === void 0) return visible.length > 0 ? "occupied" : "empty";
171293
+ try {
171294
+ for (const camera of visible) for (const subtree of subtrees) if (await dirHasAnyEntry(node_path.default.join(input.basePath, camera, subtree))) return "occupied";
171295
+ } catch {
171296
+ return "unknown";
171297
+ }
171298
+ return "empty";
171299
+ }
171300
+ var STORAGE_CLASS_ROOT_SUBTREES = { homekitClips: ["homekit-clips"] };
171301
+ function classifyPath(type, relativePath) {
171302
+ const rootSubtrees = STORAGE_CLASS_ROOT_SUBTREES[type];
171303
+ const cameraSubtrees = STORAGE_CLASS_SUBTREES[type];
171304
+ if (rootSubtrees === void 0 && cameraSubtrees === void 0) return "own";
171305
+ const segments = relativePath.split("/").filter((segment) => segment.length > 0);
171306
+ if (rootSubtrees !== void 0) {
171307
+ const top = segments[0];
171308
+ return top !== void 0 && rootSubtrees.includes(top) ? "own" : "foreign";
171309
+ }
171310
+ if (segments.length < 2) return "descend";
171311
+ const subtree = segments[1];
171312
+ return subtree !== void 0 && cameraSubtrees?.includes(subtree) === true ? "own" : "foreign";
171313
+ }
171314
+ async function collectFiles(io, scope, prefix) {
170947
171315
  const entries = await io.list(prefix);
170948
171316
  const files = [];
170949
171317
  for (const entry of entries) {
171318
+ const verdict = scope(entry);
171319
+ if (verdict === "foreign") continue;
170950
171320
  if ((await io.list(entry)).length > 0) {
170951
- files.push(...await collectFiles(io, entry));
171321
+ files.push(...await collectFiles(io, scope, entry));
170952
171322
  continue;
170953
171323
  }
170954
- files.push(entry);
171324
+ if (verdict === "own") files.push(entry);
170955
171325
  }
170956
171326
  return files;
170957
171327
  }
170958
- async function moveLocationTree(io) {
170959
- const candidates = await collectFiles(io);
171328
+ var WHOLE_ROOT = () => "own";
171329
+ async function moveLocationTree(io, scope = WHOLE_ROOT) {
171330
+ const candidates = await collectFiles(io, scope);
170960
171331
  const copied = [];
170961
171332
  let bytes = 0;
170962
171333
  for (const relativePath of candidates) {
@@ -170978,6 +171349,118 @@ var require_storage_orchestrator_addon = __commonJS({
170978
171349
  bytes
170979
171350
  };
170980
171351
  }
171352
+ var RepointRefusedError = class extends Error {
171353
+ reason;
171354
+ name = "RepointRefusedError";
171355
+ constructor(reason, message) {
171356
+ super(message);
171357
+ this.reason = reason;
171358
+ }
171359
+ };
171360
+ function rootsOverlap(a, b) {
171361
+ if (a === b) return true;
171362
+ const within = (inner, outer) => inner.startsWith(outer.endsWith("/") ? outer : `${outer}/`);
171363
+ return within(a, b) || within(b, a);
171364
+ }
171365
+ function basePathOf$1(config) {
171366
+ const raw = config["basePath"];
171367
+ return typeof raw === "string" && raw.length > 0 ? raw : null;
171368
+ }
171369
+ var LocationRepointer = class {
171370
+ deps;
171371
+ claims = /* @__PURE__ */ new Set();
171372
+ constructor(deps) {
171373
+ this.deps = deps;
171374
+ }
171375
+ /**
171376
+ * Move `stored`'s own subtree to `next`'s root, then run `commit` (the row
171377
+ * save) while the claim is still held. Throws {@link RepointRefusedError}
171378
+ * with NOTHING copied or deleted when a mover is on the root.
171379
+ */
171380
+ async run(stored, next, commit) {
171381
+ const claim = await this.claim(stored, next);
171382
+ try {
171383
+ await this.refuseWhileMovers(claim, stored);
171384
+ const moved = await moveLocationTree(await this.deps.ioFor(stored, next), (path) => classifyPath(stored.type, path));
171385
+ this.logMoved(stored, next, moved);
171386
+ return await commit();
171387
+ } finally {
171388
+ this.claims.delete(claim);
171389
+ }
171390
+ }
171391
+ /**
171392
+ * May a mover that touches `locationIds` run? Refuses while a repoint holds
171393
+ * an overlapping root. Called by the owning addon AFTER its job is
171394
+ * registered, so the repoint's mover listing and this check cannot both miss.
171395
+ */
171396
+ async clearance(locationIds) {
171397
+ const keys = await this.keysOf(locationIds);
171398
+ for (const claim of this.claims) if (claim.keys.some((held) => keys.some((key) => rootsOverlap(held, key)))) return {
171399
+ clear: false,
171400
+ reason: "repoint-active",
171401
+ repointLocationId: claim.locationId
171402
+ };
171403
+ return { clear: true };
171404
+ }
171405
+ async claim(stored, next) {
171406
+ const paths = [basePathOf$1(stored.config), basePathOf$1(next.config)].filter((path) => path !== null);
171407
+ const keys = await Promise.all(paths.map((path) => this.deps.resolveKey(path)));
171408
+ for (const other of this.claims) if (other.keys.some((held) => keys.some((key) => rootsOverlap(held, key)))) this.refuse(stored, "repoint-active", `a repoint of "${other.locationId}" holds the same root`);
171409
+ const claim = {
171410
+ locationId: stored.id,
171411
+ keys
171412
+ };
171413
+ this.claims.add(claim);
171414
+ return claim;
171415
+ }
171416
+ async refuseWhileMovers(claim, stored) {
171417
+ let movers;
171418
+ try {
171419
+ movers = await this.deps.listMovers();
171420
+ } catch (err) {
171421
+ this.claims.delete(claim);
171422
+ this.refuse(stored, "movers-unreadable", `the active movers could not be listed (${err instanceof Error ? err.message : String(err)})`);
171423
+ }
171424
+ for (const mover of movers) {
171425
+ if (mover.job.state !== "queued" && mover.job.state !== "running") continue;
171426
+ const touched = await this.keysOf(this.locationsTouchedBy(mover));
171427
+ if (claim.keys.some((held) => touched.some((key) => rootsOverlap(held, key)))) {
171428
+ this.claims.delete(claim);
171429
+ this.refuse(stored, "mover-active", `${mover.lane} mover ${mover.job.jobId} (${mover.job.fromLocationId} -> ${mover.job.toLocationId}) touches the same physical root`, {
171430
+ jobId: mover.job.jobId,
171431
+ lane: mover.lane
171432
+ });
171433
+ }
171434
+ }
171435
+ }
171436
+ locationsTouchedBy(mover) {
171437
+ return [mover.job.fromLocationId, mover.job.toLocationId];
171438
+ }
171439
+ /** `*` is the media mover's "from wherever the rows are": every eventMedia location. */
171440
+ async keysOf(locationIds) {
171441
+ const paths = locationIds.flatMap((id) => id === "*" ? this.deps.locationIdsOfType("eventMedia") : [id]).map((id) => this.deps.locationBasePath(id)).filter((path) => path !== null);
171442
+ return Promise.all(paths.map((path) => this.deps.resolveKey(path)));
171443
+ }
171444
+ refuse(stored, reason, detail, meta = {}) {
171445
+ this.deps.logger.warn("storage-orchestrator: REFUSING to re-point a location \u2014 nothing was moved", { meta: {
171446
+ id: stored.id,
171447
+ type: stored.type,
171448
+ reason,
171449
+ detail,
171450
+ ...meta
171451
+ } });
171452
+ throw new RepointRefusedError(reason, `re-point of "${stored.id}" refused (${reason}): ${detail}`);
171453
+ }
171454
+ logMoved(stored, next, moved) {
171455
+ this.deps.logger.info("storage-orchestrator: singleton location re-pointed \u2014 its data was carried over", { meta: {
171456
+ id: stored.id,
171457
+ type: stored.type,
171458
+ from: basePathOf$1(stored.config),
171459
+ to: basePathOf$1(next.config),
171460
+ ...moved
171461
+ } });
171462
+ }
171463
+ };
170981
171464
  function resolveRefusalFor(location, locality) {
170982
171465
  if (locality !== false) return null;
170983
171466
  return `storage.resolve is a request for a path this node can open with node:fs, and location "${location.id}" is served by the remote provider "${location.providerId}" (nodeLocal: false) \u2014 the path it would return exists on the remote host, not here. Read and write this location through the storage cap (read/write, or beginUpload/writeChunk/finalizeUpload) instead.`;
@@ -171287,46 +171770,6 @@ var require_storage_orchestrator_addon = __commonJS({
171287
171770
  args: envelope.args
171288
171771
  }, { timeout: HUB_SETTINGS_FORWARD_TIMEOUT_MS }));
171289
171772
  }
171290
- var STORAGE_CLASS_SUBTREES = {
171291
- eventMedia: ["events"],
171292
- recordingsLow: ["low"],
171293
- recordings: ["high", "mid"]
171294
- };
171295
- function isDotEntry(name) {
171296
- return name.startsWith(".");
171297
- }
171298
- async function dirHasAnyEntry(dir) {
171299
- let handle;
171300
- try {
171301
- handle = await (0, node_fs_promises.opendir)(dir);
171302
- } catch (err) {
171303
- if (err.code === "ENOENT") return false;
171304
- throw err;
171305
- }
171306
- try {
171307
- return await handle.read() !== null;
171308
- } finally {
171309
- await handle.close();
171310
- }
171311
- }
171312
- async function probeLocalDirectoryOccupancy(input) {
171313
- let names;
171314
- try {
171315
- names = await (0, node_fs_promises.readdir)(input.basePath);
171316
- } catch (err) {
171317
- if (err.code === "ENOENT") return "empty";
171318
- return "unknown";
171319
- }
171320
- const visible = names.filter((name) => !isDotEntry(name));
171321
- const subtrees = STORAGE_CLASS_SUBTREES[input.type];
171322
- if (subtrees === void 0) return visible.length > 0 ? "occupied" : "empty";
171323
- try {
171324
- for (const camera of visible) for (const subtree of subtrees) if (await dirHasAnyEntry(node_path.default.join(input.basePath, camera, subtree))) return "occupied";
171325
- } catch {
171326
- return "unknown";
171327
- }
171328
- return "empty";
171329
- }
171330
171773
  var DEFAULT_STALE_AFTER_MS = 6e4;
171331
171774
  var LocationOccupancySlice = class {
171332
171775
  deps;
@@ -171827,6 +172270,44 @@ var require_storage_orchestrator_addon = __commonJS({
171827
172270
  getLocalNodeId() {
171828
172271
  return this.localNode;
171829
172272
  }
172273
+ /**
172274
+ * Whether a seed must wait for an ANSWERED read of the store (D49). True on a
172275
+ * node whose locations live in a store: seeding before that read lands writes
172276
+ * defaults the store's own rows (not yet adopted, or not yet read) then
172277
+ * contradict. False on a node that has no store by design — it seeds in memory
172278
+ * at every boot.
172279
+ */
172280
+ storeRequiredForSeed = false;
172281
+ setStoreRequiredForSeed(required) {
172282
+ this.storeRequiredForSeed = required;
172283
+ }
172284
+ /**
172285
+ * The location types a default was ever seeded for — null when the store
172286
+ * cannot remember (then a type with no row is seeded, as before). What makes
172287
+ * an operator's delete of a seeded default STICK: zero rows today is not
172288
+ * "fresh install", it is only fresh when nothing was ever seeded.
172289
+ */
172290
+ seededTypes = null;
172291
+ /** The store held locations but no seed memory: an install older than the memory. */
172292
+ installPredatesSeedMemory = false;
172293
+ async loadSeedMemory(store, rowCount) {
172294
+ if (!store.loadSeededTypes) return;
172295
+ const types = await store.loadSeededTypes();
172296
+ this.seededTypes = new Set(types);
172297
+ this.installPredatesSeedMemory = types.length === 0 && rowCount > 0;
172298
+ }
172299
+ rememberSeeded(type) {
172300
+ const seeded = this.seededTypes;
172301
+ const store = this.locationStore;
172302
+ if (seeded === null || seeded.has(type) || !store?.markTypeSeeded) return;
172303
+ seeded.add(type);
172304
+ store.markTypeSeeded(type).catch((err) => {
172305
+ this.logger.error("storage-orchestrator: persisting the seed memory failed", { meta: {
172306
+ type,
172307
+ error: err instanceof Error ? err.message : String(err)
172308
+ } });
172309
+ });
172310
+ }
171830
172311
  /** True once a persistence store is wired (constructor or {@link attachStore}). */
171831
172312
  hasStore() {
171832
172313
  return this.locationStore !== null;
@@ -171860,6 +172341,7 @@ var require_storage_orchestrator_addon = __commonJS({
171860
172341
  async attachStore(store) {
171861
172342
  if (this.locationStore) return;
171862
172343
  const rows = await store.loadAll();
172344
+ await this.loadSeedMemory(store, rows.length);
171863
172345
  this.locationStore = store;
171864
172346
  const dbIds = new Set(rows.map((r) => r.id));
171865
172347
  for (const loc of rows) this.locations.set(loc.id, loc);
@@ -171998,6 +172480,7 @@ var require_storage_orchestrator_addon = __commonJS({
171998
172480
  async initialize() {
171999
172481
  if (!this.locationStore) return;
172000
172482
  const rows = await this.locationStore.loadAll();
172483
+ await this.loadSeedMemory(this.locationStore, rows.length);
172001
172484
  for (const loc of rows) this.locations.set(loc.id, loc);
172002
172485
  this.logger.info("storage-orchestrator: hydrated locations from store", { meta: { loaded: this.locations.size } });
172003
172486
  this.migrateOntoTheWriteSet(rows);
@@ -172450,14 +172933,35 @@ var require_storage_orchestrator_addon = __commonJS({
172450
172933
  * directory for derivative slots like `recordingsLow` → `recordings`).
172451
172934
  */
172452
172935
  seedDefaults(input) {
172936
+ if (this.storeRequiredForSeed && this.locationStore === null) {
172937
+ this.logger.warn("storage-orchestrator: seed deferred \u2014 the location store has not answered a read yet (not wired, adoption pending, or the read failed); seeding now could shadow the operator's existing locations");
172938
+ return {
172939
+ added: 0,
172940
+ deferred: "store-not-answered"
172941
+ };
172942
+ }
172453
172943
  let added = 0;
172944
+ let deferredProvider = false;
172454
172945
  const declById = new Map(input.declarations.map((d) => [d.id, d]));
172455
172946
  const rootFor = (d) => {
172456
172947
  const dedicated = d.defaultRoot === "media" ? input.mediaBasePath : d.defaultRoot === "backup" ? input.backupBasePath : void 0;
172457
172948
  return node_path.join(dedicated ?? input.basePath, d.id);
172458
172949
  };
172459
172950
  for (const d of input.declarations) {
172460
- if (this.hasAnyLocationOfType(d.id)) continue;
172951
+ if (this.hasAnyLocationOfType(d.id)) {
172952
+ this.rememberSeeded(d.id);
172953
+ continue;
172954
+ }
172955
+ if (this.seededTypes?.has(d.id)) continue;
172956
+ if (this.installPredatesSeedMemory) {
172957
+ this.logger.info("storage-orchestrator: existing install \u2014 not re-creating a default the operator may have removed", { meta: { type: d.id } });
172958
+ this.rememberSeeded(d.id);
172959
+ continue;
172960
+ }
172961
+ if (this.nodeLocalResolver !== null && this.nodeLocalResolver(input.providerId) === void 0) {
172962
+ deferredProvider = true;
172963
+ continue;
172964
+ }
172461
172965
  const base = d.defaultsTo ? this.getLocationById(`${d.defaultsTo}:default`)?.config["basePath"] ?? rootFor(declById.get(d.defaultsTo) ?? d) : rootFor(d);
172462
172966
  this.upsertLocation({
172463
172967
  id: `${d.id}:default`,
@@ -172468,12 +172972,18 @@ var require_storage_orchestrator_addon = __commonJS({
172468
172972
  enabled: true,
172469
172973
  isSystem: false
172470
172974
  });
172975
+ this.rememberSeeded(d.id);
172471
172976
  added++;
172472
172977
  }
172473
172978
  if (added > 0) this.logger.info("storage-orchestrator: seeded default locations", { meta: {
172474
172979
  added,
172475
172980
  basePath: input.basePath
172476
172981
  } });
172982
+ if (deferredProvider) this.logger.info("storage-orchestrator: seed deferred for some types \u2014 provider not classified yet", { meta: { providerId: input.providerId } });
172983
+ return deferredProvider ? {
172984
+ added,
172985
+ deferred: "provider-unclassified"
172986
+ } : { added };
172477
172987
  }
172478
172988
  /**
172479
172989
  * The same-type physical-root refusal (D389).
@@ -172805,29 +173315,69 @@ var require_storage_orchestrator_addon = __commonJS({
172805
173315
  lowPercent: 15,
172806
173316
  criticalPercent: 5
172807
173317
  };
172808
- function bandOf(freePercent, config) {
172809
- if (freePercent === null) return {
172810
- band: "critical",
172811
- reason: "capacity-unreadable"
172812
- };
172813
- if (freePercent <= config.criticalPercent) return {
172814
- band: "critical",
172815
- reason: "free-below-critical"
172816
- };
172817
- if (freePercent <= config.lowPercent) return {
172818
- band: "low",
172819
- reason: "free-below-warn"
173318
+ var UNMANAGED_HYSTERESIS_POINTS = 2;
173319
+ var MANAGED_LOW_ENTER = 0.8;
173320
+ var MANAGED_LOW_EXIT = 0.9;
173321
+ var MANAGED_CRITICAL_ENTER = 0.5;
173322
+ var MANAGED_CRITICAL_EXIT = 0.6;
173323
+ var MANAGED_LOW_GRACE_MS = 10 * 6e4;
173324
+ function boundsOf(floorPercent, config) {
173325
+ if (floorPercent === void 0) return {
173326
+ lowEnter: config.lowPercent,
173327
+ lowExit: config.lowPercent + UNMANAGED_HYSTERESIS_POINTS,
173328
+ criticalEnter: config.criticalPercent,
173329
+ criticalExit: config.criticalPercent + UNMANAGED_HYSTERESIS_POINTS,
173330
+ lowGraceMs: 0,
173331
+ lowReason: "free-below-warn",
173332
+ criticalReason: "free-below-critical"
172820
173333
  };
172821
173334
  return {
172822
- band: "ok",
172823
- reason: "free-ok"
173335
+ lowEnter: floorPercent * MANAGED_LOW_ENTER,
173336
+ lowExit: floorPercent * MANAGED_LOW_EXIT,
173337
+ criticalEnter: floorPercent * MANAGED_CRITICAL_ENTER,
173338
+ criticalExit: floorPercent * MANAGED_CRITICAL_EXIT,
173339
+ lowGraceMs: MANAGED_LOW_GRACE_MS,
173340
+ lowReason: "free-below-floor",
173341
+ criticalReason: "free-below-hard-bound"
172824
173342
  };
172825
173343
  }
172826
173344
  var StorageLocationBandMonitor = class {
172827
173345
  config;
173346
+ now;
172828
173347
  band = /* @__PURE__ */ new Map();
172829
- constructor(config = DEFAULT_STORAGE_LOCATION_BAND_CONFIG) {
173348
+ /** When free first went under `lowEnter` and has stayed there. */
173349
+ belowLowSince = /* @__PURE__ */ new Map();
173350
+ constructor(config = DEFAULT_STORAGE_LOCATION_BAND_CONFIG, now = Date.now) {
172830
173351
  this.config = config;
173352
+ this.now = now;
173353
+ }
173354
+ nextBand(locationId, previous, free, b) {
173355
+ if (free < b.criticalEnter || previous === "critical" && free < b.criticalExit) return {
173356
+ band: "critical",
173357
+ reason: b.criticalReason
173358
+ };
173359
+ const lowActive = previous === "low" || previous === "critical";
173360
+ if (free < b.lowEnter || lowActive && free < b.lowExit) {
173361
+ if (lowActive) return {
173362
+ band: "low",
173363
+ reason: b.lowReason
173364
+ };
173365
+ const since = this.belowLowSince.get(locationId) ?? this.now();
173366
+ this.belowLowSince.set(locationId, since);
173367
+ if (this.now() - since >= b.lowGraceMs) return {
173368
+ band: "low",
173369
+ reason: b.lowReason
173370
+ };
173371
+ return {
173372
+ band: previous ?? "ok",
173373
+ reason: "free-ok"
173374
+ };
173375
+ }
173376
+ this.belowLowSince.delete(locationId);
173377
+ return {
173378
+ band: "ok",
173379
+ reason: "free-ok"
173380
+ };
172831
173381
  }
172832
173382
  /**
172833
173383
  * Observe one sweep of managed locations. Locations missing from `samples`
@@ -172838,8 +173388,9 @@ var require_storage_orchestrator_addon = __commonJS({
172838
173388
  const out = [];
172839
173389
  for (const sample of samples) {
172840
173390
  seen.add(sample.locationId);
172841
- const { band, reason } = bandOf(sample.freePercent, this.config);
173391
+ if (sample.freePercent === null) continue;
172842
173392
  const previous = this.band.get(sample.locationId) ?? null;
173393
+ const { band, reason } = this.nextBand(sample.locationId, previous, sample.freePercent, boundsOf(sample.floorPercent, this.config));
172843
173394
  if (previous === band) continue;
172844
173395
  this.band.set(sample.locationId, band);
172845
173396
  if (previous === null && band === "ok") continue;
@@ -172849,11 +173400,17 @@ var require_storage_orchestrator_addon = __commonJS({
172849
173400
  ...sample.locationName !== void 0 ? { locationName: sample.locationName } : {},
172850
173401
  band,
172851
173402
  freePercent: sample.freePercent,
173403
+ ...sample.floorPercent !== void 0 ? { floorPercent: sample.floorPercent } : {},
173404
+ ...sample.freeBytes !== void 0 ? { freeBytes: sample.freeBytes } : {},
173405
+ ...sample.floorBytes !== void 0 ? { floorBytes: sample.floorBytes } : {},
172852
173406
  reason,
172853
173407
  previous
172854
173408
  });
172855
173409
  }
172856
- for (const id of [...this.band.keys()]) if (!seen.has(id)) this.band.delete(id);
173410
+ for (const id of [...this.band.keys()]) if (!seen.has(id)) {
173411
+ this.band.delete(id);
173412
+ this.belowLowSince.delete(id);
173413
+ }
172857
173414
  return out;
172858
173415
  }
172859
173416
  };
@@ -172866,6 +173423,7 @@ var require_storage_orchestrator_addon = __commonJS({
172866
173423
  var CAP_PROVIDER_REGISTERED_CATEGORY = "capability:provider-registered";
172867
173424
  var StorageOrchestratorAddon = class extends require_dist10.BaseAddon {
172868
173425
  locationsKind = null;
173426
+ seedMarkersKind = null;
172869
173427
  drainStateKind = null;
172870
173428
  service = null;
172871
173429
  pressureManager = null;
@@ -172891,6 +173449,8 @@ var require_storage_orchestrator_addon = __commonJS({
172891
173449
  pressureTimer = null;
172892
173450
  pressureSweepInFlight = false;
172893
173451
  migration = null;
173452
+ /** Guards every single-location repoint and arbitrates movers per physical root. */
173453
+ repointer = null;
172894
173454
  /**
172895
173455
  * Cached `providerId → nodeLocal` snapshot (SP1). Backs the orchestrator's
172896
173456
  * synchronous `NodeLocalResolver` — `getProviderInfo()` is async, so we
@@ -172927,6 +173487,7 @@ var require_storage_orchestrator_addon = __commonJS({
172927
173487
  if (records !== void 0) {
172928
173488
  await records.declare(storageOrchestratorRecordKinds);
172929
173489
  this.locationsKind = SqliteLocationStore.recordStoresOf(records);
173490
+ this.seedMarkersKind = SqliteLocationStore.seedMarkersOf(records);
172930
173491
  this.drainStateKind = SqliteDrainStateStore.recordStoresOf(records);
172931
173492
  }
172932
173493
  const getProviders = () => {
@@ -172946,6 +173507,7 @@ var require_storage_orchestrator_addon = __commonJS({
172946
173507
  return proxy;
172947
173508
  });
172948
173509
  this.service = service;
173510
+ service.setStoreRequiredForSeed(this.locationsKind !== null);
172949
173511
  service.setNodeLocalResolver((providerId) => this.nodeLocalByProvider.get(providerId));
172950
173512
  service.setOccupancyProbe((location) => this.locationOccupancy(location));
172951
173513
  await service.initialize();
@@ -172969,8 +173531,7 @@ var require_storage_orchestrator_addon = __commonJS({
172969
173531
  ...input,
172970
173532
  config: keychained
172971
173533
  };
172972
- await this.carryDataOnRepoint(stored, next);
172973
- const saved = service.upsertLocation(next);
173534
+ const saved = await this.upsertCarryingData(stored, next, () => service.upsertLocation(next));
172974
173535
  this.announceLocationsChanged(saved.id, saved.type, "upsert");
172975
173536
  return this.redacted(saved);
172976
173537
  },
@@ -173180,6 +173741,49 @@ var require_storage_orchestrator_addon = __commonJS({
173180
173741
  deviceKeyOf: (locationId) => this.locationDeviceKey(locationId)
173181
173742
  });
173182
173743
  this.migration = migration;
173744
+ this.repointer = new LocationRepointer({
173745
+ logger: this.ctx.logger,
173746
+ resolveKey: async (basePath) => {
173747
+ try {
173748
+ return await node_fs_promises.realpath(basePath);
173749
+ } catch {
173750
+ return node_path.resolve(basePath);
173751
+ }
173752
+ },
173753
+ locationBasePath: (id) => this.locationBasePath(id),
173754
+ locationIdsOfType: (type) => service.listLocations({ type }).map((loc) => loc.id),
173755
+ listMovers: () => migration.movers(),
173756
+ ioFor: async (stored, next) => {
173757
+ const target = {
173758
+ ...stored,
173759
+ config: next.config
173760
+ };
173761
+ const [source, destination] = await Promise.all([service.getProviderFor(stored), service.getProviderFor(target)]);
173762
+ return {
173763
+ list: (prefix) => source.list(prefix === void 0 ? { location: stored } : {
173764
+ location: stored,
173765
+ prefix
173766
+ }),
173767
+ read: (relativePath) => source.read({
173768
+ location: stored,
173769
+ relativePath
173770
+ }),
173771
+ write: (relativePath, data) => destination.write({
173772
+ location: target,
173773
+ relativePath,
173774
+ data
173775
+ }),
173776
+ readBack: (relativePath) => destination.read({
173777
+ location: target,
173778
+ relativePath
173779
+ }),
173780
+ deleteSource: (relativePath) => source.delete({
173781
+ location: stored,
173782
+ relativePath
173783
+ })
173784
+ };
173785
+ }
173786
+ });
173183
173787
  const cleanup = new StorageCleanupCoordinator({
173184
173788
  locations: service,
173185
173789
  state: this.state("storage-cleanup", require_dist10.StorageCleanupJobSchema.nullable(), null),
@@ -173215,6 +173819,13 @@ var require_storage_orchestrator_addon = __commonJS({
173215
173819
  status: ({ jobId }) => migration.status(jobId),
173216
173820
  cancel: async ({ jobId }) => ({ cancelled: await migration.cancel(jobId) }),
173217
173821
  movers: () => migration.movers(),
173822
+ moverClearance: async ({ locationIds }) => {
173823
+ const verdict = await this.requireRepointer().clearance(locationIds);
173824
+ return verdict.clear ? { clear: true } : {
173825
+ clear: false,
173826
+ repointLocationId: verdict.repointLocationId
173827
+ };
173828
+ },
173218
173829
  residue: () => migration.residue(),
173219
173830
  drain: async (input) => ({ jobId: await migration.drain(input) }),
173220
173831
  cleanupStart: async (input) => ({ jobId: await cleanup.start(input) }),
@@ -173342,7 +173953,7 @@ var require_storage_orchestrator_addon = __commonJS({
173342
173953
  else this.ctx.logger.info("storage-orchestrator: this node boots no data-store engine \u2014 storage locations are seeded from addon declarations at every boot, and node-pinned locations live on the hub");
173343
173954
  return null;
173344
173955
  }
173345
- return new SqliteLocationStore(engine, this.locationsKind);
173956
+ return new SqliteLocationStore(engine, this.locationsKind, this.seedMarkersKind);
173346
173957
  }
173347
173958
  /**
173348
173959
  * Late-wire the persistence store once a `data-store-provider` engine
@@ -173354,12 +173965,26 @@ var require_storage_orchestrator_addon = __commonJS({
173354
173965
  * restart — the service never hydrates from / persists to SQLite.
173355
173966
  */
173356
173967
  storeWireInFlight = null;
173968
+ /** True while the locations kind is empty and its legacy table still holds rows. */
173969
+ async adoptionIsPending() {
173970
+ const records = this.ctx.records;
173971
+ if (records === void 0 || this.locationsKind === null) return false;
173972
+ try {
173973
+ await records.declare(storageOrchestratorRecordKinds);
173974
+ return false;
173975
+ } catch (err) {
173976
+ const message = err instanceof Error ? err.message : String(err);
173977
+ this.ctx.logger.warn("storage-orchestrator: persistence NOT wired \u2014 the storage-locations kind is not authoritative yet (adoption pending or the check failed); seeding is deferred", { meta: { error: message } });
173978
+ return true;
173979
+ }
173980
+ }
173357
173981
  async ensureStoreWired() {
173358
173982
  const service = this.service;
173359
173983
  if (!service || service.hasStore()) return;
173360
173984
  if (this.storeWireInFlight) return this.storeWireInFlight;
173361
173985
  const store = this.resolveLocationStore(true);
173362
173986
  if (!store) return;
173987
+ if (await this.adoptionIsPending()) return;
173363
173988
  this.ctx.logger.info("storage-orchestrator: data-store engine now available \u2014 wiring persistence");
173364
173989
  this.storeWireInFlight = service.attachStore(store).then(() => this.seedFromDeclarations()).catch((err) => {
173365
173990
  this.ctx.logger.error("storage-orchestrator: attachStore failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
@@ -173388,7 +174013,7 @@ var require_storage_orchestrator_addon = __commonJS({
173388
174013
  }
173389
174014
  /**
173390
174015
  * Sample free space for every managed location and emit NC band edges.
173391
- * A failed capacity read stays `null` (critical) — never folded to 0 (D393).
174016
+ * A failed capacity read stays `null` and changes no band — never folded to 0 (D393).
173392
174017
  */
173393
174018
  async emitLocationBandEvents() {
173394
174019
  const svc = this.service;
@@ -173404,11 +174029,18 @@ var require_storage_orchestrator_addon = __commonJS({
173404
174029
  };
173405
174030
  const capacity = await this.volumeCapacityOf(locationId).catch(() => null);
173406
174031
  const freePercent = capacity === null ? null : freePercentOf(capacity);
174032
+ if (capacity === null) ctx.logger.debug("storage: location capacity unreadable \u2014 band unchanged", { meta: { locationId } });
174033
+ const floorPercent = thresholds.get(locationId);
173407
174034
  return {
173408
174035
  locationId,
173409
174036
  nodeId: loc.nodeId,
173410
174037
  locationName: loc.displayName,
173411
- freePercent
174038
+ freePercent,
174039
+ ...floorPercent !== void 0 ? { floorPercent } : {},
174040
+ ...capacity !== null ? {
174041
+ freeBytes: capacity.availableBytes,
174042
+ ...floorPercent !== void 0 ? { floorBytes: Math.round(capacity.totalBytes * floorPercent / 100) } : {}
174043
+ } : {}
173412
174044
  };
173413
174045
  }));
173414
174046
  for (const edge of this.locationBandMonitor.observe(samples)) {
@@ -173417,6 +174049,9 @@ var require_storage_orchestrator_addon = __commonJS({
173417
174049
  locationId: edge.locationId,
173418
174050
  band: edge.band,
173419
174051
  freePercent: edge.freePercent,
174052
+ floorPercent: edge.floorPercent ?? null,
174053
+ freeBytes: edge.freeBytes ?? null,
174054
+ floorBytes: edge.floorBytes ?? null,
173420
174055
  reason: edge.reason
173421
174056
  } });
173422
174057
  ctx.eventBus.emit({
@@ -173531,50 +174166,19 @@ var require_storage_orchestrator_addon = __commonJS({
173531
174166
  * A no-op for everything else — a rename, a mode change, a `multi` kind, or a
173532
174167
  * location being created for the first time.
173533
174168
  */
173534
- async carryDataOnRepoint(stored, next) {
173535
- if (stored === void 0) return;
174169
+ async upsertCarryingData(stored, next, commit) {
174170
+ if (stored === void 0) return commit();
173536
174171
  const service = this.service;
173537
- if (service === null) return;
173538
- if (service.listDeclarations().find((entry) => entry.id === next.type)?.cardinality !== "single") return;
174172
+ if (service === null) return commit();
174173
+ if (service.listDeclarations().find((entry) => entry.id === next.type)?.cardinality !== "single") return commit();
173539
174174
  const from = stored.config["basePath"];
173540
174175
  const to = next.config["basePath"];
173541
- if (typeof from !== "string" || typeof to !== "string" || from === to) return;
173542
- const source = await service.getProviderFor(stored);
173543
- const target = {
173544
- ...stored,
173545
- config: next.config
173546
- };
173547
- const destination = await service.getProviderFor(target);
173548
- const moved = await moveLocationTree({
173549
- list: (prefix) => source.list(prefix === void 0 ? { location: stored } : {
173550
- location: stored,
173551
- prefix
173552
- }),
173553
- read: (relativePath) => source.read({
173554
- location: stored,
173555
- relativePath
173556
- }),
173557
- write: (relativePath, data) => destination.write({
173558
- location: target,
173559
- relativePath,
173560
- data
173561
- }),
173562
- readBack: (relativePath) => destination.read({
173563
- location: target,
173564
- relativePath
173565
- }),
173566
- deleteSource: (relativePath) => source.delete({
173567
- location: stored,
173568
- relativePath
173569
- })
173570
- });
173571
- this.ctx.logger.info("storage-orchestrator: singleton location re-pointed \u2014 its data was carried over", { meta: {
173572
- id: stored.id,
173573
- type: stored.type,
173574
- from,
173575
- to,
173576
- ...moved
173577
- } });
174176
+ if (typeof from !== "string" || typeof to !== "string" || from === to) return commit();
174177
+ return this.requireRepointer().run(stored, next, commit);
174178
+ }
174179
+ requireRepointer() {
174180
+ if (this.repointer === null) throw new Error("storage-orchestrator: not initialized");
174181
+ return this.repointer;
173578
174182
  }
173579
174183
  secretKeysFor(providerId, config) {
173580
174184
  return secretKeysForConfig(config, this.declaredSecretKeys(providerId));
@@ -173978,11 +174582,11 @@ var require_storage_orchestrator = __commonJS({
173978
174582
  }
173979
174583
  });
173980
174584
 
173981
- // ../system/dist/constants-BPWA-r-Y.js
173982
- var require_constants_BPWA_r_Y = __commonJS({
173983
- "../system/dist/constants-BPWA-r-Y.js"(exports) {
174585
+ // ../system/dist/constants-DBGG6Zu5.js
174586
+ var require_constants_DBGG6Zu5 = __commonJS({
174587
+ "../system/dist/constants-DBGG6Zu5.js"(exports) {
173984
174588
  "use strict";
173985
- var MESH_CA_KEY_REF = require_dist_Ct88catN().buildSecretRef({
174589
+ var MESH_CA_KEY_REF = require_dist_Bi4Ioxop().buildSecretRef({
173986
174590
  owner: "system",
173987
174591
  scope: { kind: "global" },
173988
174592
  fieldPath: "meshCaKey"
@@ -174045,14 +174649,14 @@ var require_constants_BPWA_r_Y = __commonJS({
174045
174649
  }
174046
174650
  });
174047
174651
 
174048
- // ../system/dist/agent-https-CMz4S56_.js
174049
- var require_agent_https_CMz4S56 = __commonJS({
174050
- "../system/dist/agent-https-CMz4S56_.js"(exports) {
174652
+ // ../system/dist/agent-https-B7IjRRzT.js
174653
+ var require_agent_https_B7IjRRzT = __commonJS({
174654
+ "../system/dist/agent-https-B7IjRRzT.js"(exports) {
174051
174655
  "use strict";
174052
174656
  var require_chunk = require_chunk_Cek0wNdY();
174053
- var require_dist10 = require_dist_Ct88catN();
174657
+ var require_dist10 = require_dist_Bi4Ioxop();
174054
174658
  var require_x509_es = require_x509_es_C4cOo48M();
174055
- var require_constants15 = require_constants_BPWA_r_Y();
174659
+ var require_constants15 = require_constants_DBGG6Zu5();
174056
174660
  var zod = require_zod();
174057
174661
  var node_crypto = __require("crypto");
174058
174662
  var _camstack_types_node = require_node();
@@ -192795,11 +193399,11 @@ var require_system_config_addon = __commonJS({
192795
193399
  [Symbol.toStringTag]: { value: "Module" }
192796
193400
  });
192797
193401
  var require_chunk = require_chunk_Cek0wNdY();
192798
- var require_dist10 = require_dist_Ct88catN();
193402
+ var require_dist10 = require_dist_Bi4Ioxop();
192799
193403
  var require_safe_buffer$1 = require_safe_buffer_D3aoa_7X();
192800
193404
  var require_x509_es = require_x509_es_C4cOo48M();
192801
- var require_constants$8 = require_constants_BPWA_r_Y();
192802
- var require_agent_https = require_agent_https_CMz4S56();
193405
+ var require_constants$8 = require_constants_DBGG6Zu5();
193406
+ var require_agent_https = require_agent_https_B7IjRRzT();
192803
193407
  var require_src$1 = require_src_CnAqG6EF();
192804
193408
  var zod = require_zod();
192805
193409
  var node_crypto = __require("crypto");
@@ -272512,7 +273116,7 @@ var require_winston_logging = __commonJS({
272512
273116
  [Symbol.toStringTag]: { value: "Module" }
272513
273117
  });
272514
273118
  var require_chunk = require_chunk_Cek0wNdY();
272515
- var require_dist10 = require_dist_Ct88catN();
273119
+ var require_dist10 = require_dist_Bi4Ioxop();
272516
273120
  var require_formatter = require_formatter_DqAKDlvN();
272517
273121
  var node_path = __require("path");
272518
273122
  node_path = require_chunk.__toESM(node_path);
@@ -280162,14 +280766,14 @@ var require_lib_8oZrELLm = __commonJS({
280162
280766
  }
280163
280767
  });
280164
280768
 
280165
- // ../system/dist/hub-mdns-ciao-DAU1WttG.js
280166
- var require_hub_mdns_ciao_DAU1WttG = __commonJS({
280167
- "../system/dist/hub-mdns-ciao-DAU1WttG.js"(exports) {
280769
+ // ../system/dist/hub-mdns-ciao-2FzLWfbD.js
280770
+ var require_hub_mdns_ciao_2FzLWfbD = __commonJS({
280771
+ "../system/dist/hub-mdns-ciao-2FzLWfbD.js"(exports) {
280168
280772
  "use strict";
280169
280773
  var require_chunk = require_chunk_Cek0wNdY();
280170
- var require_dist10 = require_dist_Ct88catN();
280774
+ var require_dist10 = require_dist_Bi4Ioxop();
280171
280775
  var require_x509_es = require_x509_es_C4cOo48M();
280172
- var require_agent_https = require_agent_https_CMz4S56();
280776
+ var require_agent_https = require_agent_https_B7IjRRzT();
280173
280777
  var node_crypto = __require("crypto");
280174
280778
  var _camstack_types_node = require_node();
280175
280779
  var node_fs_promises = __require("fs/promises");
@@ -281168,12 +281772,12 @@ var require_platform_key_source_Di7HrlGR = __commonJS({
281168
281772
  }
281169
281773
  });
281170
281774
 
281171
- // ../system/dist/restore-archive-Cprdh3P9.js
281172
- var require_restore_archive_Cprdh3P9 = __commonJS({
281173
- "../system/dist/restore-archive-Cprdh3P9.js"(exports) {
281775
+ // ../system/dist/restore-archive-C0ymYQk7.js
281776
+ var require_restore_archive_C0ymYQk7 = __commonJS({
281777
+ "../system/dist/restore-archive-C0ymYQk7.js"(exports) {
281174
281778
  "use strict";
281175
281779
  var require_chunk = require_chunk_Cek0wNdY();
281176
- var require_dist10 = require_dist_Ct88catN();
281780
+ var require_dist10 = require_dist_Bi4Ioxop();
281177
281781
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
281178
281782
  var zod = require_zod();
281179
281783
  var node_crypto = __require("crypto");
@@ -292494,9 +293098,9 @@ var require_dist3 = __commonJS({
292494
293098
  "use strict";
292495
293099
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
292496
293100
  var require_chunk = require_chunk_Cek0wNdY();
292497
- var require_dist10 = require_dist_Ct88catN();
293101
+ var require_dist10 = require_dist_Bi4Ioxop();
292498
293102
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
292499
- var require_device_ca = require_device_ca_75rt3U_U();
293103
+ var require_device_ca = require_device_ca_B3hUVfYk();
292500
293104
  var require_builtins_alerts_alerts_addon = require_alerts_addon();
292501
293105
  require_alerts();
292502
293106
  var require_builtins_calendars_calendars_addon = require_calendars_addon();
@@ -292521,21 +293125,21 @@ var require_dist3 = __commonJS({
292521
293125
  var require_builtins_native_metrics_native_metrics_addon = require_native_metrics_addon();
292522
293126
  var require_builtins_device_history_device_history_addon = require_device_history_addon();
292523
293127
  var require_builtins_sqlite_storage_filesystem_storage_addon = require_filesystem_storage_addon();
292524
- var require_wal_maintenance = require_wal_maintenance_mwCBe3Vr();
293128
+ var require_wal_maintenance = require_wal_maintenance_Bo_lo3Dh();
292525
293129
  var require_builtins_sqlite_storage_sqlite_settings_addon = require_sqlite_settings_addon();
292526
293130
  var require_builtins_sqlite_storage_index = require_sqlite_storage();
292527
293131
  var require_builtins_storage_orchestrator_storage_orchestrator_addon = require_storage_orchestrator_addon();
292528
293132
  require_storage_orchestrator();
292529
- var require_constants15 = require_constants_BPWA_r_Y();
292530
- var require_agent_https = require_agent_https_CMz4S56();
293133
+ var require_constants15 = require_constants_DBGG6Zu5();
293134
+ var require_agent_https = require_agent_https_B7IjRRzT();
292531
293135
  var require_builtins_system_config_system_config_addon = require_system_config_addon();
292532
293136
  require_system_config();
292533
293137
  var require_builtins_winston_logging_index = require_winston_logging();
292534
293138
  var require_custom_action_registry = require_custom_action_registry_COyb49zm();
292535
- var require_hub_mdns_ciao = require_hub_mdns_ciao_DAU1WttG();
293139
+ var require_hub_mdns_ciao = require_hub_mdns_ciao_2FzLWfbD();
292536
293140
  var require_platform_key_source = require_platform_key_source_Di7HrlGR();
292537
293141
  var require_resource_monitor = require_resource_monitor_mio9dSCT();
292538
- var require_restore_archive = require_restore_archive_Cprdh3P9();
293142
+ var require_restore_archive = require_restore_archive_C0ymYQk7();
292539
293143
  var zod = require_zod();
292540
293144
  var node_crypto = __require("crypto");
292541
293145
  node_crypto = require_chunk.__toESM(node_crypto);
@@ -301599,8 +302203,16 @@ var require_dist3 = __commonJS({
301599
302203
  const nodeId = typeof picked2.nodeId === "string" ? picked2.nodeId : void 0;
301600
302204
  const topLevelAddonId = typeof picked2.addonId === "string" ? picked2.addonId : void 0;
301601
302205
  const canRecoverNestedAddonId = !require_dist10.isArrayOutputSchema(method.schema.output) && !require_dist10.objectInputDeclaresAddonId(method.schema.input);
301602
- const addonId = topLevelAddonId ?? (canRecoverNestedAddonId ? require_dist10.extractNestedAddonId(rest2) : void 0);
301603
- return invokeCapMethod(resolveProviderForCall(capName, nodeId, () => getLocal(ctx, addonId), services, topLevelAddonId), method, method.callerRequired ? injectCaller(rest2, ctx, capName, method.name, services) : rest2, capName, isCollection, services);
302206
+ let addonId = topLevelAddonId ?? (canRecoverNestedAddonId ? require_dist10.extractNestedAddonId(rest2) : void 0);
302207
+ const resolveAndInvoke = async () => {
302208
+ let refusePin = topLevelAddonId;
302209
+ if (addonId === void 0 && !method.isArrayOutput && services.inferCollectionAddonPin) {
302210
+ addonId = await Promise.resolve(services.inferCollectionAddonPin(capName, method.name, rest2));
302211
+ if (addonId !== void 0) refusePin = addonId;
302212
+ }
302213
+ return invokeCapMethod(resolveProviderForCall(capName, nodeId, () => getLocal(ctx, addonId), services, refusePin), method, method.callerRequired ? injectCaller(rest2, ctx, capName, method.name, services) : rest2, capName, isCollection, services);
302214
+ };
302215
+ return resolveAndInvoke();
301604
302216
  }
301605
302217
  const { rest, picked } = stripKeys(input, ["nodeId"]);
301606
302218
  return invokeCapMethod(resolveProviderForCall(capName, typeof picked.nodeId === "string" ? picked.nodeId : void 0, () => getLocal(ctx), services), method, method.callerRequired ? injectCaller(rest, ctx, capName, method.name, services) : rest, capName, isCollection, services);
@@ -315973,7 +316585,7 @@ var require_enums = __commonJS({
315973
316585
  "../types/dist/enums.js"(exports) {
315974
316586
  "use strict";
315975
316587
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
315976
- var require_event_category = require_event_category_Hoe_mqbd();
316588
+ var require_event_category = require_event_category_eSqkye_K();
315977
316589
  var EventSourceType = /* @__PURE__ */ (function(EventSourceType2) {
315978
316590
  EventSourceType2["Addon"] = "addon";
315979
316591
  EventSourceType2["Core"] = "core";
@@ -315993,8 +316605,8 @@ var require_dist4 = __commonJS({
315993
316605
  "../types/dist/index.js"(exports) {
315994
316606
  "use strict";
315995
316607
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
315996
- var require_sleep = require_sleep_L6qY125();
315997
- var require_event_category = require_event_category_Hoe_mqbd();
316608
+ var require_sleep = require_sleep_BE7_6UnC();
316609
+ var require_event_category = require_event_category_eSqkye_K();
315998
316610
  var require_composition = require_composition_eoYYVfOp();
315999
316611
  var require_node_color2 = require_node_color();
316000
316612
  var require_err_msg = require_err_msg_BhVbr0NX();
@@ -318405,6 +319017,31 @@ var require_dist4 = __commonJS({
318405
319017
  error: zod.z.string().nullable()
318406
319018
  });
318407
319019
  var StorageCleanupStatusInputSchema = zod.z.object({ jobId: zod.z.string().optional() });
319020
+ async function startMoverUnderClearance(deps) {
319021
+ const refuse = (detail) => {
319022
+ throw new Error(`mover refused: ${detail}`);
319023
+ };
319024
+ const ask = async () => {
319025
+ try {
319026
+ return await deps.clearance(deps.locationIds);
319027
+ } catch (err) {
319028
+ return refuse(`the storage orchestrator could not be asked (${err instanceof Error ? err.message : String(err)})`);
319029
+ }
319030
+ };
319031
+ const blocked = (answer) => `a re-point of "${answer.repointLocationId ?? "a location"}" holds an overlapping storage root`;
319032
+ const before = await ask();
319033
+ if (!before.clear) return refuse(blocked(before));
319034
+ const jobId = deps.start();
319035
+ const after = await ask().catch((err) => {
319036
+ deps.cancel(jobId);
319037
+ throw err;
319038
+ });
319039
+ if (!after.clear) {
319040
+ deps.cancel(jobId);
319041
+ return refuse(blocked(after));
319042
+ }
319043
+ return jobId;
319044
+ }
318408
319045
  var SUB_DETECTION_TYPES = ["face", "plate"];
318409
319046
  var RECOGNITION_TYPES = [
318410
319047
  "face",
@@ -321402,7 +322039,14 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
321402
322039
  ]);
321403
322040
  var AlertActionSchema = zod.z.object({
321404
322041
  label: zod.z.string(),
321405
- href: zod.z.string().optional()
322042
+ href: zod.z.string().optional(),
322043
+ /**
322044
+ * How `href` is followed. Absent / `app`: an admin-ui route (client-side).
322045
+ * `server`: a route the HUB serves (an addon HTTP route such as an OAuth
322046
+ * start) — the browser must leave the SPA with a full navigation, because
322047
+ * the SPA has no such route and would bounce to the home page.
322048
+ */
322049
+ navigation: zod.z.enum(["app", "server"]).optional()
321406
322050
  });
321407
322051
  var AlertStatusSchema = zod.z.enum([
321408
322052
  "active",
@@ -327702,7 +328346,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
327702
328346
  var deviceAdoptionCapability = {
327703
328347
  name: "device-adoption",
327704
328348
  scope: "system",
327705
- mode: "singleton",
328349
+ mode: "collection",
327706
328350
  status: {
327707
328351
  schema: AdoptionStatusSchema,
327708
328352
  kind: "poll"
@@ -349070,7 +349714,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
349070
349714
  auth: "admin"
349071
349715
  }),
349072
349716
  /** List candidate filter chips for an integration via its device-adoption
349073
- * provider — addonId-routed so it never hits the singleton shadow. */
349717
+ * provider — addonId-routed via the collection pin. */
349074
349718
  adoptionListCandidateFilters: require_sleep.method(zod.z.object({
349075
349719
  addonId: zod.z.string(),
349076
349720
  integrationId: zod.z.string()
@@ -352917,6 +353561,9 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
352917
353561
  "server-updated",
352918
353562
  "export-completed",
352919
353563
  "backup-completed",
353564
+ "alert-critical",
353565
+ "alert-error",
353566
+ "alert-critical-resolved",
352920
353567
  "camera-online",
352921
353568
  "camera-offline",
352922
353569
  "camera-disabled",
@@ -354055,6 +354702,18 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
354055
354702
  value: "backup-completed",
354056
354703
  label: "Backup completed"
354057
354704
  },
354705
+ {
354706
+ value: "alert-critical",
354707
+ label: "Critical alert raised"
354708
+ },
354709
+ {
354710
+ value: "alert-error",
354711
+ label: "Error alert raised"
354712
+ },
354713
+ {
354714
+ value: "alert-critical-resolved",
354715
+ label: "Critical alert resolved"
354716
+ },
354058
354717
  {
354059
354718
  value: "alarm-arm-refused",
354060
354719
  label: "Alarm arm refused"
@@ -363041,6 +363700,18 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
363041
363700
  */
363042
363701
  movers: require_sleep.method(zod.z.object({}), zod.z.array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }),
363043
363702
  /**
363703
+ * May a mover that touches these locations run NOW? One claim per physical
363704
+ * root is owned by the storage orchestrator: a single-location repoint holds
363705
+ * it while it carries its subtree, and a mover (`relocateMedia`,
363706
+ * `relocateFootage`, a migration move) asks here AFTER registering its job and
363707
+ * cancels itself on `clear: false`. The repoint, in turn, refuses while a
363708
+ * registered mover touches its root. Asked, never polled (D3).
363709
+ */
363710
+ moverClearance: require_sleep.method(zod.z.object({ locationIds: zod.z.array(zod.z.string()).readonly() }), zod.z.object({
363711
+ clear: zod.z.boolean(),
363712
+ repointLocationId: zod.z.string().optional()
363713
+ }), { auth: "admin" }),
363714
+ /**
363044
363715
  * What each class's SOURCE still holds, from the archive — never from the
363045
363716
  * resident index (D295). Only classes with something left (or something
363046
363717
  * unknown) are listed, so an empty list means there is nothing to drain and
@@ -367380,35 +368051,139 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
367380
368051
  "error",
367381
368052
  "unavailable"
367382
368053
  ]);
368054
+ var DishwasherOperationStateSchema = zod.z.enum([
368055
+ "Inactive",
368056
+ "Ready",
368057
+ "DelayedStart",
368058
+ "Run",
368059
+ "Pause",
368060
+ "ActionRequired",
368061
+ "Finished",
368062
+ "Error",
368063
+ "Aborting"
368064
+ ]);
368065
+ var DishwasherAvailabilitySchema = zod.z.enum([
368066
+ "available",
368067
+ "offline",
368068
+ "description-missing"
368069
+ ]);
367383
368070
  var DishwasherConsumableLevelSchema = zod.z.enum([
367384
368071
  "ok",
367385
368072
  "low",
367386
368073
  "empty"
367387
368074
  ]);
367388
368075
  var DishwasherStatusSchema = zod.z.object({
368076
+ availability: DishwasherAvailabilitySchema,
368077
+ /** Coarse state for compact surfaces; `unavailable` whenever the appliance state is not known. */
367389
368078
  state: DishwasherStateSchema,
367390
- /** Active program id/name, or null when none selected / unknown. */
368079
+ /** The appliance's operation state as reported; null when not known. */
368080
+ operationState: DishwasherOperationStateSchema.nullable(),
368081
+ /** Feature key of the ACTIVE (running) program, or null when none runs / not known. */
367391
368082
  program: zod.z.string().nullable(),
367392
- /** Program tokens the hardware accepts — drives the UI selector. */
368083
+ /** Feature key of the SELECTED program (what Start would run), or null when none / not known. */
368084
+ selectedProgram: zod.z.string().nullable(),
368085
+ /** Feature keys of the programs the appliance currently offers — drives the UI selector. */
367393
368086
  availablePrograms: zod.z.array(zod.z.string()),
367394
- /** Minutes remaining on the active cycle, or null when not running. */
367395
- remainingMinutes: zod.z.number().int().nonnegative().nullable(),
368087
+ /** Seconds remaining on the active cycle, or null when not running / not known. */
368088
+ remainingSeconds: zod.z.number().int().nonnegative().nullable(),
368089
+ /** Seconds the delayed start is set to (time until the cycle starts), or null. */
368090
+ delayedStartSeconds: zod.z.number().int().nonnegative().nullable(),
367396
368091
  /** Cycle completion 0..100, or null when not applicable. */
367397
368092
  progressPercent: zod.z.number().min(0).max(100).nullable(),
367398
- /** True when the door is reported open. */
367399
- doorOpen: zod.z.boolean(),
367400
- /** True when remote start is allowed (door closed, power on, etc.). */
367401
- remoteStartAllowed: zod.z.boolean(),
367402
- /** Main power / switched-on state. */
367403
- powerOn: zod.z.boolean(),
368093
+ /** True when the door is reported open; null when the door state is not known. */
368094
+ doorOpen: zod.z.boolean().nullable(),
368095
+ /** True when the door is latched (locked) during a cycle; null when not reported. */
368096
+ doorLocked: zod.z.boolean().nullable(),
368097
+ /** True when remote start is allowed (door closed, power on, etc.); null when not known. */
368098
+ remoteStartAllowed: zod.z.boolean().nullable(),
368099
+ /** True when the appliance accepts remote control now; null when not known. */
368100
+ remoteControlActive: zod.z.boolean().nullable(),
368101
+ /** True while somebody operates the appliance at its panel; null when not reported. */
368102
+ localControlActive: zod.z.boolean().nullable(),
368103
+ /** Main power / switched-on state; null when not known. */
368104
+ powerOn: zod.z.boolean().nullable(),
368105
+ /** The raw power state (`On`, `Off`, `Standby`, …); null when not known. */
368106
+ powerState: zod.z.string().nullable(),
367404
368107
  saltLevel: DishwasherConsumableLevelSchema.nullable(),
367405
368108
  rinseAidLevel: DishwasherConsumableLevelSchema.nullable(),
367406
- halfLoad: zod.z.boolean(),
367407
- hygienePlus: zod.z.boolean(),
368109
+ /** Option states: null when the appliance does not expose the option or has not said. */
368110
+ halfLoad: zod.z.boolean().nullable(),
368111
+ hygienePlus: zod.z.boolean().nullable(),
368112
+ /** Extra dry option when the appliance exposes it. */
368113
+ extraDry: zod.z.boolean().nullable(),
368114
+ /** Intensive zone (lower basket) when the appliance exposes it. */
368115
+ intensiveZone: zod.z.boolean().nullable(),
368116
+ /** VarioSpeed / VarioSpeed+ when the appliance exposes it. */
368117
+ varioSpeedPlus: zod.z.boolean().nullable(),
368118
+ /** Child lock (controls disabled on the appliance); null when not known. */
368119
+ childLock: zod.z.boolean().nullable(),
368120
+ /** Feature keys of the events currently raised (salt low, rinse aid low, program finished, …). */
368121
+ activeEvents: zod.z.array(zod.z.string()),
368122
+ /** Numeric error code when the appliance reports one; null otherwise. */
367408
368123
  errorCode: zod.z.number().nullable(),
368124
+ /** Name of the raised alert-level event while the appliance is in error; null otherwise. */
367409
368125
  errorLabel: zod.z.string().nullable(),
368126
+ /** Ms epoch of the last value received from the appliance; 0 = never. */
367410
368127
  lastFetchedAt: zod.z.number()
367411
368128
  });
368129
+ var DeviceIdSchema = zod.z.number().int().nonnegative();
368130
+ var DishwasherValueSchema = zod.z.union([
368131
+ zod.z.boolean(),
368132
+ zod.z.string(),
368133
+ zod.z.number()
368134
+ ]);
368135
+ var DishwasherAccessSchema = zod.z.enum([
368136
+ "none",
368137
+ "read",
368138
+ "readWrite",
368139
+ "writeOnly",
368140
+ "readStatic"
368141
+ ]);
368142
+ var DishwasherOptionDescriptorSchema = zod.z.object({
368143
+ /** Feature key (`Dishcare.Dishwasher.Option.ExtraDry`). */
368144
+ key: zod.z.string(),
368145
+ /** Last segment of the key, for display. */
368146
+ name: zod.z.string(),
368147
+ type: zod.z.enum([
368148
+ "boolean",
368149
+ "number",
368150
+ "enum",
368151
+ "string"
368152
+ ]),
368153
+ access: DishwasherAccessSchema,
368154
+ /** The appliance reports this entity as currently available. */
368155
+ available: zod.z.boolean(),
368156
+ /** Unit the description declares for a number; null when it declares none. */
368157
+ unit: zod.z.string().nullable(),
368158
+ min: zod.z.number().nullable(),
368159
+ max: zod.z.number().nullable(),
368160
+ step: zod.z.number().nullable(),
368161
+ /** Allowed members of an enum; null for other types. */
368162
+ enumValues: zod.z.array(zod.z.string()).nullable(),
368163
+ defaultValue: DishwasherValueSchema.nullable()
368164
+ });
368165
+ var DishwasherProgramSchema = zod.z.object({
368166
+ /** Feature key (`Dishcare.Dishwasher.Program.Eco50`) — what `setProgram` / `start` take. */
368167
+ key: zod.z.string(),
368168
+ name: zod.z.string(),
368169
+ /** The appliance currently offers this program. */
368170
+ available: zod.z.boolean(),
368171
+ /** The options this program lists, with the program-level access / availability / default. */
368172
+ options: zod.z.array(DishwasherOptionDescriptorSchema)
368173
+ });
368174
+ var DishwasherOptionsSchema = zod.z.object({
368175
+ availability: DishwasherAvailabilitySchema,
368176
+ /** What the appliance can be told to do, from the commands its description declares. */
368177
+ supportsStart: zod.z.boolean(),
368178
+ supportsPause: zod.z.boolean(),
368179
+ supportsResume: zod.z.boolean(),
368180
+ supportsStop: zod.z.boolean(),
368181
+ supportsPower: zod.z.boolean(),
368182
+ /** Every program the description lists, with its options. */
368183
+ programs: zod.z.array(DishwasherProgramSchema),
368184
+ /** Every option and setting entity the description lists (programs reference these by key). */
368185
+ options: zod.z.array(DishwasherOptionDescriptorSchema)
368186
+ });
367412
368187
  var dishwasherCapability = {
367413
368188
  name: "dishwasher",
367414
368189
  scope: "device",
@@ -367416,58 +368191,102 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
367416
368191
  mode: "singleton",
367417
368192
  deviceTypes: [require_err_msg.DeviceType.Dishwasher],
367418
368193
  methods: {
367419
- start: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.void(), {
368194
+ /**
368195
+ * Start a program. Without `program` the selected one starts. `options` are
368196
+ * option feature keys; each is checked against the program's constraints.
368197
+ */
368198
+ start: require_sleep.method(zod.z.object({
368199
+ deviceId: DeviceIdSchema,
368200
+ program: zod.z.string().min(1).optional(),
368201
+ options: zod.z.record(zod.z.string(), DishwasherValueSchema).optional()
368202
+ }), zod.z.void(), {
367420
368203
  kind: "mutation",
367421
368204
  auth: "protected"
367422
368205
  }),
367423
- pause: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.void(), {
368206
+ pause: require_sleep.method(zod.z.object({ deviceId: DeviceIdSchema }), zod.z.void(), {
367424
368207
  kind: "mutation",
367425
368208
  auth: "protected"
367426
368209
  }),
367427
- stop: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.void(), {
368210
+ resume: require_sleep.method(zod.z.object({ deviceId: DeviceIdSchema }), zod.z.void(), {
368211
+ kind: "mutation",
368212
+ auth: "protected"
368213
+ }),
368214
+ stop: require_sleep.method(zod.z.object({ deviceId: DeviceIdSchema }), zod.z.void(), {
367428
368215
  kind: "mutation",
367429
368216
  auth: "protected"
367430
368217
  }),
368218
+ /** Select (not start) a program; refused when the appliance does not offer it. */
367431
368219
  setProgram: require_sleep.method(zod.z.object({
367432
- deviceId: zod.z.number().int().nonnegative(),
368220
+ deviceId: DeviceIdSchema,
367433
368221
  program: zod.z.string().min(1)
367434
368222
  }), zod.z.void(), {
367435
368223
  kind: "mutation",
367436
368224
  auth: "protected"
367437
368225
  }),
367438
368226
  setPower: require_sleep.method(zod.z.object({
367439
- deviceId: zod.z.number().int().nonnegative(),
368227
+ deviceId: DeviceIdSchema,
367440
368228
  on: zod.z.boolean()
367441
368229
  }), zod.z.void(), {
367442
368230
  kind: "mutation",
367443
368231
  auth: "protected"
367444
368232
  }),
367445
368233
  setHalfLoad: require_sleep.method(zod.z.object({
367446
- deviceId: zod.z.number().int().nonnegative(),
368234
+ deviceId: DeviceIdSchema,
367447
368235
  on: zod.z.boolean()
367448
368236
  }), zod.z.void(), {
367449
368237
  kind: "mutation",
367450
368238
  auth: "protected"
367451
368239
  }),
367452
368240
  setHygienePlus: require_sleep.method(zod.z.object({
367453
- deviceId: zod.z.number().int().nonnegative(),
368241
+ deviceId: DeviceIdSchema,
368242
+ on: zod.z.boolean()
368243
+ }), zod.z.void(), {
368244
+ kind: "mutation",
368245
+ auth: "protected"
368246
+ }),
368247
+ setExtraDry: require_sleep.method(zod.z.object({
368248
+ deviceId: DeviceIdSchema,
367454
368249
  on: zod.z.boolean()
367455
368250
  }), zod.z.void(), {
367456
368251
  kind: "mutation",
367457
368252
  auth: "protected"
367458
368253
  }),
368254
+ setIntensiveZone: require_sleep.method(zod.z.object({
368255
+ deviceId: DeviceIdSchema,
368256
+ on: zod.z.boolean()
368257
+ }), zod.z.void(), {
368258
+ kind: "mutation",
368259
+ auth: "protected"
368260
+ }),
368261
+ setVarioSpeedPlus: require_sleep.method(zod.z.object({
368262
+ deviceId: DeviceIdSchema,
368263
+ on: zod.z.boolean()
368264
+ }), zod.z.void(), {
368265
+ kind: "mutation",
368266
+ auth: "protected"
368267
+ }),
368268
+ setChildLock: require_sleep.method(zod.z.object({
368269
+ deviceId: DeviceIdSchema,
368270
+ on: zod.z.boolean()
368271
+ }), zod.z.void(), {
368272
+ kind: "mutation",
368273
+ auth: "protected"
368274
+ }),
368275
+ /**
368276
+ * Write any option or setting by feature key. Refused when the description does
368277
+ * not list it, it is not writable or not available now, or the value is outside
368278
+ * its declared range / members.
368279
+ */
367459
368280
  setOption: require_sleep.method(zod.z.object({
367460
- deviceId: zod.z.number().int().nonnegative(),
368281
+ deviceId: DeviceIdSchema,
367461
368282
  key: zod.z.string().min(1),
367462
- value: zod.z.union([
367463
- zod.z.boolean(),
367464
- zod.z.string(),
367465
- zod.z.number()
367466
- ])
368283
+ value: DishwasherValueSchema
367467
368284
  }), zod.z.void(), {
367468
368285
  kind: "mutation",
367469
368286
  auth: "protected"
367470
- })
368287
+ }),
368288
+ /** What this appliance can do, from its own description — the UI is derived from it. */
368289
+ getOptions: require_sleep.method(zod.z.object({ deviceId: DeviceIdSchema }), DishwasherOptionsSchema, { auth: "protected" })
367471
368290
  },
367472
368291
  status: {
367473
368292
  schema: DishwasherStatusSchema,
@@ -379588,6 +380407,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
379588
380407
  "fan",
379589
380408
  "vacuum",
379590
380409
  "lawn-mower",
380410
+ "dishwasher",
379591
380411
  "light",
379592
380412
  "switch",
379593
380413
  "siren",
@@ -382486,6 +383306,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
382486
383306
  addonId: null,
382487
383307
  access: "view"
382488
383308
  },
383309
+ "dishwasher.getOptions": {
383310
+ capName: "dishwasher",
383311
+ capScope: "device",
383312
+ addonId: null,
383313
+ access: "view"
383314
+ },
382489
383315
  "dishwasher.getStatus": {
382490
383316
  capName: "dishwasher",
382491
383317
  capScope: "device",
@@ -382498,6 +383324,24 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
382498
383324
  addonId: null,
382499
383325
  access: "create"
382500
383326
  },
383327
+ "dishwasher.resume": {
383328
+ capName: "dishwasher",
383329
+ capScope: "device",
383330
+ addonId: null,
383331
+ access: "create"
383332
+ },
383333
+ "dishwasher.setChildLock": {
383334
+ capName: "dishwasher",
383335
+ capScope: "device",
383336
+ addonId: null,
383337
+ access: "create"
383338
+ },
383339
+ "dishwasher.setExtraDry": {
383340
+ capName: "dishwasher",
383341
+ capScope: "device",
383342
+ addonId: null,
383343
+ access: "create"
383344
+ },
382501
383345
  "dishwasher.setHalfLoad": {
382502
383346
  capName: "dishwasher",
382503
383347
  capScope: "device",
@@ -382510,6 +383354,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
382510
383354
  addonId: null,
382511
383355
  access: "create"
382512
383356
  },
383357
+ "dishwasher.setIntensiveZone": {
383358
+ capName: "dishwasher",
383359
+ capScope: "device",
383360
+ addonId: null,
383361
+ access: "create"
383362
+ },
382513
383363
  "dishwasher.setOption": {
382514
383364
  capName: "dishwasher",
382515
383365
  capScope: "device",
@@ -382528,6 +383378,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
382528
383378
  addonId: null,
382529
383379
  access: "create"
382530
383380
  },
383381
+ "dishwasher.setVarioSpeedPlus": {
383382
+ capName: "dishwasher",
383383
+ capScope: "device",
383384
+ addonId: null,
383385
+ access: "create"
383386
+ },
382531
383387
  "dishwasher.start": {
382532
383388
  capName: "dishwasher",
382533
383389
  capScope: "device",
@@ -386974,6 +387830,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
386974
387830
  addonId: null,
386975
387831
  access: "view"
386976
387832
  },
387833
+ "storageMigration.moverClearance": {
387834
+ capName: "storage-migration",
387835
+ capScope: "system",
387836
+ addonId: null,
387837
+ access: "view"
387838
+ },
386977
387839
  "storageMigration.movers": {
386978
387840
  capName: "storage-migration",
386979
387841
  capScope: "system",
@@ -392322,6 +393184,11 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
392322
393184
  form: "single",
392323
393185
  optional: false
392324
393186
  }],
393187
+ "dishwasher.getOptions": [{
393188
+ name: "deviceId",
393189
+ form: "single",
393190
+ optional: false
393191
+ }],
392325
393192
  "dishwasher.getStatus": [{
392326
393193
  name: "deviceId",
392327
393194
  form: "single",
@@ -392332,6 +393199,21 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
392332
393199
  form: "single",
392333
393200
  optional: false
392334
393201
  }],
393202
+ "dishwasher.resume": [{
393203
+ name: "deviceId",
393204
+ form: "single",
393205
+ optional: false
393206
+ }],
393207
+ "dishwasher.setChildLock": [{
393208
+ name: "deviceId",
393209
+ form: "single",
393210
+ optional: false
393211
+ }],
393212
+ "dishwasher.setExtraDry": [{
393213
+ name: "deviceId",
393214
+ form: "single",
393215
+ optional: false
393216
+ }],
392335
393217
  "dishwasher.setHalfLoad": [{
392336
393218
  name: "deviceId",
392337
393219
  form: "single",
@@ -392342,6 +393224,11 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
392342
393224
  form: "single",
392343
393225
  optional: false
392344
393226
  }],
393227
+ "dishwasher.setIntensiveZone": [{
393228
+ name: "deviceId",
393229
+ form: "single",
393230
+ optional: false
393231
+ }],
392345
393232
  "dishwasher.setOption": [{
392346
393233
  name: "deviceId",
392347
393234
  form: "single",
@@ -392357,6 +393244,11 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
392357
393244
  form: "single",
392358
393245
  optional: false
392359
393246
  }],
393247
+ "dishwasher.setVarioSpeedPlus": [{
393248
+ name: "deviceId",
393249
+ form: "single",
393250
+ optional: false
393251
+ }],
392360
393252
  "dishwasher.start": [{
392361
393253
  name: "deviceId",
392362
393254
  form: "single",
@@ -395946,6 +396838,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
395946
396838
  status: (input) => dispatch("storageMigration", "status", "query", input),
395947
396839
  cancel: (input) => dispatch("storageMigration", "cancel", "mutation", input),
395948
396840
  movers: (input) => dispatch("storageMigration", "movers", "query", input),
396841
+ moverClearance: (input) => dispatch("storageMigration", "moverClearance", "query", input),
395949
396842
  residue: (input) => dispatch("storageMigration", "residue", "query", input),
395950
396843
  drain: (input) => dispatch("storageMigration", "drain", "mutation", input),
395951
396844
  cleanupStart: (input) => dispatch("storageMigration", "cleanupStart", "mutation", input),
@@ -396483,7 +397376,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
396483
397376
  case "deviceTypes":
396484
397377
  return kind.startsWith("device-") || kind === "detection-blind";
396485
397378
  case "nodeIds":
396486
- return kind.startsWith("node-") || kind === "addon-crash-loop" || kind === "addon-update-available" || kind === "addon-updated" || kind === "server-update-available" || kind === "wrapper-update-available" || kind === "server-updated" || kind.startsWith("storage-location-");
397379
+ return kind.startsWith("node-") || kind === "addon-crash-loop" || kind === "addon-update-available" || kind === "addon-updated" || kind === "server-update-available" || kind === "wrapper-update-available" || kind === "server-updated" || kind.startsWith("storage-location-") || kind.startsWith("alert-");
396487
397380
  case "packageNames":
396488
397381
  return kind.endsWith("update-available") || kind === "addon-updated" || kind === "node-updating" || kind === "server-updated";
396489
397382
  case "consumableLowPercent":
@@ -396532,6 +397425,9 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
396532
397425
  "wrapper-update-available": "system",
396533
397426
  "backup-completed": "system",
396534
397427
  "export-completed": "system",
397428
+ "alert-critical": "system",
397429
+ "alert-error": "system",
397430
+ "alert-critical-resolved": "system",
396535
397431
  "storage-location-low": "system",
396536
397432
  "storage-location-critical": "system",
396537
397433
  "storage-location-ok": "system",
@@ -396667,6 +397563,16 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
396667
397563
  "storage-location-ok"
396668
397564
  ]
396669
397565
  },
397566
+ {
397567
+ id: "alerts",
397568
+ family: "system",
397569
+ label: "Alerts",
397570
+ kinds: [
397571
+ "alert-critical",
397572
+ "alert-error",
397573
+ "alert-critical-resolved"
397574
+ ]
397575
+ },
396670
397576
  {
396671
397577
  id: "alarm",
396672
397578
  family: "alarm",
@@ -397739,6 +398645,69 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
397739
398645
  return sensorKinds.every((kind) => NC_SENSOR_TRANSITION_KINDS.includes(normaliseKind(kind)));
397740
398646
  }
397741
398647
  var NC_SENSOR_TRANSITION_UNSUPPORTED_REASON = "Only a sensor that raises and clears (water, smoke, gas, CO, vibration, tamper, motion, contact) can fire on one edge. Pick only such sensor kinds, or the rule fires on every change.";
398648
+ var AlertEpisodePayloadSchema = zod.z.object({
398649
+ phase: zod.z.enum(["raised", "resolved"]),
398650
+ episodeId: zod.z.string().min(1),
398651
+ alertId: zod.z.string().min(1),
398652
+ severity: AlertSeveritySchema,
398653
+ category: zod.z.string(),
398654
+ title: zod.z.string(),
398655
+ message: zod.z.string(),
398656
+ sourceType: zod.z.string().optional(),
398657
+ sourceId: zod.z.string().optional(),
398658
+ nodeId: zod.z.string().optional(),
398659
+ /** The alert's own action link (where the operator acts on it), when it has one. */
398660
+ actionUrl: zod.z.string().optional(),
398661
+ at: zod.z.number()
398662
+ });
398663
+ function parseAlertEpisodePayload(data) {
398664
+ const parsed = AlertEpisodePayloadSchema.safeParse(data);
398665
+ return parsed.success ? parsed.data : null;
398666
+ }
398667
+ function isAlertEpisodeOpen(status) {
398668
+ return status !== "completed" && status !== "dismissed";
398669
+ }
398670
+ function isAlertEpisodeSeverity(severity) {
398671
+ return severity === "critical" || severity === "error";
398672
+ }
398673
+ function alertEpisodeId(alert) {
398674
+ const minted = alert.metadata?.["episodeId"];
398675
+ return typeof minted === "string" && minted.length > 0 ? minted : `${alert.id}@${String(alert.createdAt)}`;
398676
+ }
398677
+ function alertNodeId(alert) {
398678
+ const direct = alert.metadata?.["nodeId"];
398679
+ if (typeof direct === "string") return direct;
398680
+ const runner = alert.metadata?.["runner"];
398681
+ if (typeof runner === "object" && runner !== null && "nodeId" in runner) {
398682
+ const id = runner.nodeId;
398683
+ if (typeof id === "string") return id;
398684
+ }
398685
+ return alert.source?.type === "node" ? alert.source.id : void 0;
398686
+ }
398687
+ function alertEpisodeKind(severity, phase) {
398688
+ if (severity === "critical") return phase === "raised" ? "alert-critical" : "alert-critical-resolved";
398689
+ if (severity === "error" && phase === "raised") return "alert-error";
398690
+ return null;
398691
+ }
398692
+ function alertEpisodePayload(alert, phase, at) {
398693
+ const nodeId = alertNodeId(alert);
398694
+ return {
398695
+ phase,
398696
+ episodeId: alertEpisodeId(alert),
398697
+ alertId: alert.id,
398698
+ severity: alert.severity,
398699
+ category: alert.category,
398700
+ title: alert.title,
398701
+ message: alert.message,
398702
+ ...alert.source !== void 0 ? {
398703
+ sourceType: alert.source.type,
398704
+ sourceId: alert.source.id
398705
+ } : {},
398706
+ ...nodeId !== void 0 ? { nodeId } : {},
398707
+ ...alert.action?.href !== void 0 ? { actionUrl: alert.action.href } : {},
398708
+ at
398709
+ };
398710
+ }
397742
398711
  var RULE_BODY_ONLY = ["body"];
397743
398712
  var RULE_TEXT = ["title", "body"];
397744
398713
  var BATTERY = ["device-battery-low", "device-battery-normal"];
@@ -397748,6 +398717,11 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
397748
398717
  "device-tank-full",
397749
398718
  "device-tank-normal"
397750
398719
  ];
398720
+ var ALERT_EPISODE = [
398721
+ "alert-critical",
398722
+ "alert-error",
398723
+ "alert-critical-resolved"
398724
+ ];
397751
398725
  var UPDATES = [
397752
398726
  "addon-update-available",
397753
398727
  "server-update-available",
@@ -398269,7 +399243,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
398269
399243
  "server-updated",
398270
399244
  "storage-location-low",
398271
399245
  "storage-location-critical",
398272
- "storage-location-ok"
399246
+ "storage-location-ok",
399247
+ ...ALERT_EPISODE
398273
399248
  ]
398274
399249
  },
398275
399250
  {
@@ -398291,7 +399266,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
398291
399266
  "server-updated",
398292
399267
  "storage-location-low",
398293
399268
  "storage-location-critical",
398294
- "storage-location-ok"
399269
+ "storage-location-ok",
399270
+ ...ALERT_EPISODE
398295
399271
  ]
398296
399272
  },
398297
399273
  {
@@ -398358,7 +399334,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
398358
399334
  families: ["rule"],
398359
399335
  fields: [...RULE_TEXT],
398360
399336
  kinds: ["system"],
398361
- systemEventKinds: ["recording-degraded"]
399337
+ systemEventKinds: ["recording-degraded", ...ALERT_EPISODE]
398362
399338
  },
398363
399339
  {
398364
399340
  name: "locationName",
@@ -398555,13 +399531,53 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
398555
399531
  },
398556
399532
  {
398557
399533
  name: "title",
398558
- label: "Export title",
399534
+ label: "Export or alert title",
398559
399535
  example: "Ingresso 08:00",
398560
399536
  group: "system",
398561
399537
  families: ["rule"],
398562
399538
  fields: [...RULE_TEXT],
398563
399539
  kinds: ["system"],
398564
- systemEventKinds: ["export-completed"]
399540
+ systemEventKinds: ["export-completed", ...ALERT_EPISODE]
399541
+ },
399542
+ {
399543
+ name: "severity",
399544
+ label: "Alert severity",
399545
+ example: "critical",
399546
+ group: "system",
399547
+ families: ["rule"],
399548
+ fields: [...RULE_TEXT],
399549
+ kinds: ["system"],
399550
+ systemEventKinds: [...ALERT_EPISODE]
399551
+ },
399552
+ {
399553
+ name: "category",
399554
+ label: "Alert category",
399555
+ example: "addon.runner-failed",
399556
+ group: "system",
399557
+ families: ["rule"],
399558
+ fields: [...RULE_TEXT],
399559
+ kinds: ["system"],
399560
+ systemEventKinds: [...ALERT_EPISODE]
399561
+ },
399562
+ {
399563
+ name: "source",
399564
+ label: "Alert source (addon, node or runner)",
399565
+ example: "recorder",
399566
+ group: "system",
399567
+ families: ["rule"],
399568
+ fields: [...RULE_TEXT],
399569
+ kinds: ["system"],
399570
+ systemEventKinds: [...ALERT_EPISODE]
399571
+ },
399572
+ {
399573
+ name: "actionUrl",
399574
+ label: "Alert action link",
399575
+ example: "/system/addons",
399576
+ group: "system",
399577
+ families: ["rule"],
399578
+ fields: [...RULE_TEXT],
399579
+ kinds: ["system"],
399580
+ systemEventKinds: [...ALERT_EPISODE]
398565
399581
  },
398566
399582
  {
398567
399583
  name: "exportId",
@@ -398757,7 +399773,10 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
398757
399773
  "device-tank-full",
398758
399774
  "device-tank-normal",
398759
399775
  "node-restarting",
398760
- "node-updating"
399776
+ "node-updating",
399777
+ "alert-critical",
399778
+ "alert-error",
399779
+ "alert-critical-resolved"
398761
399780
  ];
398762
399781
  function servedTemplateVars() {
398763
399782
  return NC_TEMPLATE_VARS.filter((d) => !isHeldBackTemplateVar(d)).map((d) => d.systemEventKinds === void 0 ? d : {
@@ -401958,6 +402977,52 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
401958
402977
  }
401959
402978
  current[parts[parts.length - 1]] = value;
401960
402979
  }
402980
+ var OAUTH_LANDING_PATH = "/sso-complete";
402981
+ var OAUTH_LANDING_REASONS = [
402982
+ "access_denied",
402983
+ "invalid_state",
402984
+ "invalid_request",
402985
+ "unknown_broker",
402986
+ "not_configured",
402987
+ "exchange_failed",
402988
+ "no_refresh_token",
402989
+ "unavailable",
402990
+ "unknown"
402991
+ ];
402992
+ function isOauthLandingReason(value) {
402993
+ return OAUTH_LANDING_REASONS.some((reason) => reason === value);
402994
+ }
402995
+ function oauthLandingUrl(origin, outcome) {
402996
+ const params = new URLSearchParams({
402997
+ return: "oauth",
402998
+ status: outcome.status
402999
+ });
403000
+ if (outcome.addonId !== void 0) params.set("addon", outcome.addonId);
403001
+ if (outcome.brokerId !== void 0) params.set("broker", outcome.brokerId);
403002
+ if (outcome.integration !== void 0) params.set("integration", outcome.integration);
403003
+ if (outcome.status === "error") params.set("reason", outcome.reason ?? "unknown");
403004
+ if (outcome.action === "disconnect") params.set("action", "disconnect");
403005
+ return `${origin === null ? "" : origin.replace(/\/+$/, "")}${OAUTH_LANDING_PATH}#${params.toString()}`;
403006
+ }
403007
+ function parseOauthLandingFragment(hash) {
403008
+ if (!hash.startsWith("#")) return null;
403009
+ const params = new URLSearchParams(hash.slice(1));
403010
+ if (params.get("return") !== "oauth") return null;
403011
+ const status = params.get("status");
403012
+ if (status !== "success" && status !== "error") return null;
403013
+ const addonId = params.get("addon");
403014
+ const brokerId = params.get("broker");
403015
+ const integration = params.get("integration");
403016
+ const reasonRaw = params.get("reason");
403017
+ return {
403018
+ status,
403019
+ ...addonId !== null && addonId.length > 0 ? { addonId } : {},
403020
+ ...brokerId !== null && brokerId.length > 0 ? { brokerId } : {},
403021
+ ...integration !== null && integration.length > 0 ? { integration } : {},
403022
+ ...status === "error" ? { reason: isOauthLandingReason(reasonRaw) ? reasonRaw : "unknown" } : {},
403023
+ ...params.get("action") === "disconnect" ? { action: "disconnect" } : {}
403024
+ };
403025
+ }
401961
403026
  var MB = 1024 * 1024;
401962
403027
  var DEFAULT_POOL_MEMORY_POLICY = {
401963
403028
  sampleIntervalMs: 6e4,
@@ -402810,6 +403875,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
402810
403875
  exports.AlarmArmModeSchema = AlarmArmModeSchema;
402811
403876
  exports.AlarmPanelStatusSchema = AlarmPanelStatusSchema;
402812
403877
  exports.AlarmStateSchema = AlarmStateSchema;
403878
+ exports.AlertEpisodePayloadSchema = AlertEpisodePayloadSchema;
402813
403879
  exports.AlertSchema = AlertSchema;
402814
403880
  exports.AlertSeveritySchema = AlertSeveritySchema;
402815
403881
  exports.AlertSourceSchema = AlertSourceSchema;
@@ -403288,9 +404354,16 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
403288
404354
  exports.DiscoveredChildStatusSchema = DiscoveredChildStatusSchema;
403289
404355
  exports.DiscoveredDeviceSchema = DiscoveredDeviceSchema;
403290
404356
  exports.DiscoveredTargetSchema = DiscoveredTargetSchema;
404357
+ exports.DishwasherAccessSchema = DishwasherAccessSchema;
404358
+ exports.DishwasherAvailabilitySchema = DishwasherAvailabilitySchema;
403291
404359
  exports.DishwasherConsumableLevelSchema = DishwasherConsumableLevelSchema;
404360
+ exports.DishwasherOperationStateSchema = DishwasherOperationStateSchema;
404361
+ exports.DishwasherOptionDescriptorSchema = DishwasherOptionDescriptorSchema;
404362
+ exports.DishwasherOptionsSchema = DishwasherOptionsSchema;
404363
+ exports.DishwasherProgramSchema = DishwasherProgramSchema;
403292
404364
  exports.DishwasherStateSchema = DishwasherStateSchema;
403293
404365
  exports.DishwasherStatusSchema = DishwasherStatusSchema;
404366
+ exports.DishwasherValueSchema = DishwasherValueSchema;
403294
404367
  exports.DiskReconcileJobSchema = DiskReconcileJobSchema;
403295
404368
  exports.DisposerChain = require_sleep.DisposerChain;
403296
404369
  exports.DoorbellPressEventSchema = DoorbellPressEventSchema;
@@ -403958,6 +405031,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
403958
405031
  exports.NotificationSchema = NotificationSchema;
403959
405032
  exports.NotifierStatusSchema = NotifierStatusSchema;
403960
405033
  exports.NumericSensorStatusSchema = NumericSensorStatusSchema;
405034
+ exports.OAUTH_LANDING_PATH = OAUTH_LANDING_PATH;
405035
+ exports.OAUTH_LANDING_REASONS = OAUTH_LANDING_REASONS;
403961
405036
  exports.OPERATOR_WRITTEN_STALE_MS = OPERATOR_WRITTEN_STALE_MS;
403962
405037
  exports.OPS_LOG_DEFAULT_LIMIT = OPS_LOG_DEFAULT_LIMIT;
403963
405038
  exports.OPS_LOG_RING_DEFAULT_MAX = OPS_LOG_RING_DEFAULT_MAX;
@@ -404638,6 +405713,10 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
404638
405713
  exports.alarmPanelCapability = alarmPanelCapability;
404639
405714
  exports.alarmPatchLanded = alarmPatchLanded;
404640
405715
  exports.alarmProfileById = alarmProfileById;
405716
+ exports.alertEpisodeId = alertEpisodeId;
405717
+ exports.alertEpisodeKind = alertEpisodeKind;
405718
+ exports.alertEpisodePayload = alertEpisodePayload;
405719
+ exports.alertNodeId = alertNodeId;
404641
405720
  exports.alertsCapability = alertsCapability;
404642
405721
  exports.allDoActions = allDoActions;
404643
405722
  exports.allowedSuppliedEffect = allowedSuppliedEffect;
@@ -404994,6 +406073,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
404994
406073
  exports.invocationFromEncodeProfile = require_composition.invocationFromEncodeProfile;
404995
406074
  exports.isAgentOnlyPlacement = isAgentOnlyPlacement;
404996
406075
  exports.isAlarmControllableCap = isAlarmControllableCap;
406076
+ exports.isAlertEpisodeOpen = isAlertEpisodeOpen;
406077
+ exports.isAlertEpisodeSeverity = isAlertEpisodeSeverity;
404997
406078
  exports.isAppLinkBase = isAppLinkBase;
404998
406079
  exports.isAppStartRefusal = isAppStartRefusal;
404999
406080
  exports.isArrayOutputSchema = isArrayOutputSchema;
@@ -405029,6 +406110,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
405029
406110
  exports.isLocationEnabled = isLocationEnabled;
405030
406111
  exports.isMediaOwnerType = isMediaOwnerType;
405031
406112
  exports.isNode = isNode;
406113
+ exports.isOauthLandingReason = isOauthLandingReason;
405032
406114
  exports.isObjectInput = isObjectInput;
405033
406115
  exports.isOccupancyRule = isOccupancyRule;
405034
406116
  exports.isOwnerOnlyEventCategory = require_event_category.isOwnerOnlyEventCategory;
@@ -405152,6 +406234,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
405152
406234
  exports.notifierCapability = notifierCapability;
405153
406235
  exports.numericSensorCapability = numericSensorCapability;
405154
406236
  exports.oauthIntegrationCapability = oauthIntegrationCapability;
406237
+ exports.oauthLandingUrl = oauthLandingUrl;
405155
406238
  exports.objectInputDeclaresAddonId = objectInputDeclaresAddonId;
405156
406239
  exports.objectInputDeclaresRequiredDeviceId = objectInputDeclaresRequiredDeviceId;
405157
406240
  exports.occupancyScope = occupancyScope;
@@ -405164,6 +406247,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
405164
406247
  exports.overlayClusterStepSettings = overlayClusterStepSettings;
405165
406248
  exports.packagePickerItems = packagePickerItems;
405166
406249
  exports.parseAdminBase = parseAdminBase;
406250
+ exports.parseAlertEpisodePayload = parseAlertEpisodePayload;
405167
406251
  exports.parseAppAuthCallback = parseAppAuthCallback;
405168
406252
  exports.parseAppFlowRequest = parseAppFlowRequest;
405169
406253
  exports.parseAppPageLang = parseAppPageLang;
@@ -405174,6 +406258,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
405174
406258
  exports.parseJsonObject = require_sleep.parseJsonObject;
405175
406259
  exports.parseJsonUnknown = require_sleep.parseJsonUnknown;
405176
406260
  exports.parseMediaOwnerKey = parseMediaOwnerKey;
406261
+ exports.parseOauthLandingFragment = parseOauthLandingFragment;
405177
406262
  exports.parseOccupancyScope = parseOccupancyScope;
405178
406263
  exports.parseProcStatus = parseProcStatus;
405179
406264
  exports.parseProfileBrokerId = require_sleep.parseProfileBrokerId;
@@ -405376,6 +406461,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
405376
406461
  exports.snapshotCapability = snapshotCapability;
405377
406462
  exports.splitTranslationRef = splitTranslationRef;
405378
406463
  exports.ssoBridgeCapability = ssoBridgeCapability;
406464
+ exports.startMoverUnderClearance = startMoverUnderClearance;
405379
406465
  exports.startReachabilityPoll = startReachabilityPoll;
405380
406466
  exports.stateLabel = stateLabel;
405381
406467
  exports.stateVocabularyFor = stateVocabularyFor;
@@ -571574,6 +572660,10 @@ var require_app_page_shell = __commonJS({
571574
572660
  `.error:empty{display:none}`,
571575
572661
  `.btn:focus-visible,.link:focus-visible{outline:2px solid var(--color-primary);outline-offset:2px}`,
571576
572662
  `code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;overflow-wrap:anywhere}`,
572663
+ `.field{display:flex;flex-direction:column;gap:var(--spacing-md)}`,
572664
+ `.field label{font-size:12px;line-height:16px;font-weight:500;color:var(--color-foreground-muted)}`,
572665
+ `input[type=url],input[type=text]{width:100%;min-height:44px;padding:10px 12px;border:1px solid var(--color-border);border-radius:var(--radius-lg);background:var(--color-background);color:var(--color-foreground);font:inherit}`,
572666
+ `input[type=url]:focus-visible,input[type=text]:focus-visible{outline:2px solid var(--color-primary);outline-offset:2px}`,
571577
572667
  `form{display:contents}`,
571578
572668
  `.actions{display:flex;flex-direction:column;gap:var(--spacing-md)}`
571579
572669
  ].join("");
@@ -571717,6 +572807,7 @@ var require_oauth2_routes = __commonJS({
571717
572807
  exports.isRedirectUriAllowed = isRedirectUriAllowed;
571718
572808
  exports.summariseScopes = summariseScopes;
571719
572809
  exports.registerOauth2Routes = registerOauth2Routes;
572810
+ var types_1 = require_dist4();
571720
572811
  var session_cookie_js_1 = require_session_cookie();
571721
572812
  var app_page_strings_js_1 = require_app_page_strings();
571722
572813
  var consent_page_js_1 = require_consent_page();
@@ -571863,7 +572954,7 @@ var require_oauth2_routes = __commonJS({
571863
572954
  }
571864
572955
  return scopes.map((s) => s.type).join(", ") || "no permissions";
571865
572956
  }
571866
- function refuseAuthorize(reply, logger, method, call, refusal) {
572957
+ function refuseAuthorize(request, reply, logger, method, call, refusal) {
571867
572958
  logger.warn(`oauth2 authorize refused: ${refusal.error}`, {
571868
572959
  meta: {
571869
572960
  method,
@@ -571876,7 +572967,18 @@ var require_oauth2_routes = __commonJS({
571876
572967
  if (refusal.retryAfterSec !== void 0) {
571877
572968
  void reply.header("Retry-After", String(refusal.retryAfterSec));
571878
572969
  }
571879
- return reply.status(refusal.status).send({ error: refusal.error, ...refusal.detail });
572970
+ return answerRefusal(request, reply, refusal.status, { error: refusal.error, ...refusal.detail }, refusal.status === 503 ? "unavailable" : "invalid_request", typeof refusal.detail?.received === "string" ? refusal.detail.received : void 0);
572971
+ }
572972
+ function answerRefusal(request, reply, status, body, reason, integration) {
572973
+ const accept = request.headers.accept;
572974
+ if (typeof accept === "string" && accept.includes("text/html")) {
572975
+ return reply.redirect((0, types_1.oauthLandingUrl)(null, {
572976
+ status: "error",
572977
+ reason,
572978
+ ...integration !== void 0 && /^[A-Za-z0-9._-]{1,64}$/.test(integration) ? { integration } : {}
572979
+ }));
572980
+ }
572981
+ return reply.status(status).send(body);
571880
572982
  }
571881
572983
  function refuseUnauthenticated(request, reply, logger, call, reason) {
571882
572984
  const redirected = (0, session_cookie_js_1.shouldRedirectToLogin)(request.method, request.headers.accept);
@@ -571925,13 +573027,13 @@ var require_oauth2_routes = __commonJS({
571925
573027
  }
571926
573028
  const registry = deps.getRegistry();
571927
573029
  if (!registry) {
571928
- return reply.status(503).send({ error: "service_unavailable" });
573030
+ return answerRefusal(request, reply, 503, { error: "service_unavailable" }, "unavailable");
571929
573031
  }
571930
573032
  const { descriptors, pendingAddons } = await readIntegrationRegistry(registry);
571931
573033
  const query = request.query;
571932
573034
  const v = validateAuthorizeQuery(query, descriptors, pendingAddons);
571933
573035
  if (!v.ok) {
571934
- return refuseAuthorize(reply, deps.logger, "GET", call, v);
573036
+ return refuseAuthorize(request, reply, deps.logger, "GET", call, v);
571935
573037
  }
571936
573038
  const descriptor = descriptors.get(v.integration);
571937
573039
  if (!isRedirectUriAllowed(v.redirectUri, descriptor.allowedRedirectPrefixes, descriptor.allowedPrivateHostPaths ?? [])) {
@@ -571945,7 +573047,7 @@ var require_oauth2_routes = __commonJS({
571945
573047
  allowedPrivateHostPaths: descriptor.allowedPrivateHostPaths ?? []
571946
573048
  }
571947
573049
  });
571948
- return reply.status(400).send({ error: "invalid_request \u2014 redirect_uri not allowed for this integration" });
573050
+ return answerRefusal(request, reply, 400, { error: "invalid_request \u2014 redirect_uri not allowed for this integration" }, "invalid_request", v.integration);
571949
573051
  }
571950
573052
  const html = (0, consent_page_js_1.renderConsentPage)({
571951
573053
  lang: (0, app_page_strings_js_1.resolveAppPageLang)({
@@ -572002,7 +573104,7 @@ var require_oauth2_routes = __commonJS({
572002
573104
  }
572003
573105
  const registry = deps.getRegistry();
572004
573106
  if (!registry) {
572005
- return reply.status(503).send({ error: "service_unavailable" });
573107
+ return answerRefusal(request, reply, 503, { error: "service_unavailable" }, "unavailable");
572006
573108
  }
572007
573109
  const { descriptors, pendingAddons } = await readIntegrationRegistry(registry);
572008
573110
  const body = request.body;
@@ -572016,7 +573118,7 @@ var require_oauth2_routes = __commonJS({
572016
573118
  };
572017
573119
  const v = validateAuthorizeQuery(formQuery, descriptors, pendingAddons);
572018
573120
  if (!v.ok) {
572019
- return refuseAuthorize(reply, deps.logger, "POST", call, v);
573121
+ return refuseAuthorize(request, reply, deps.logger, "POST", call, v);
572020
573122
  }
572021
573123
  const descriptor = descriptors.get(v.integration);
572022
573124
  if (!isRedirectUriAllowed(v.redirectUri, descriptor.allowedRedirectPrefixes, descriptor.allowedPrivateHostPaths ?? [])) {
@@ -572030,7 +573132,7 @@ var require_oauth2_routes = __commonJS({
572030
573132
  allowedPrivateHostPaths: descriptor.allowedPrivateHostPaths ?? []
572031
573133
  }
572032
573134
  });
572033
- return reply.status(400).send({ error: "invalid_request \u2014 redirect_uri not allowed for this integration" });
573135
+ return answerRefusal(request, reply, 400, { error: "invalid_request \u2014 redirect_uri not allowed for this integration" }, "invalid_request", v.integration);
572034
573136
  }
572035
573137
  if (body.consent !== "allow") {
572036
573138
  return reply.redirect(`${v.redirectUri}?error=access_denied&state=${encodeURIComponent(v.state)}`);
@@ -581597,6 +582699,40 @@ var require_cap_router_runtime = __commonJS({
581597
582699
  iterableSubscription: trpc_middleware_js_1.iterableSubscription
581598
582700
  };
581599
582701
  }
582702
+ async function inferDeviceAdoptionCollectionAddonPin(methodName, input, deps) {
582703
+ const integrationId = typeof input.integrationId === "string" && input.integrationId.length > 0 ? input.integrationId : void 0;
582704
+ const camDeviceId = typeof input.camDeviceId === "number" && Number.isInteger(input.camDeviceId) ? input.camDeviceId : void 0;
582705
+ if (methodName === "resync" || camDeviceId !== void 0 && integrationId === void 0) {
582706
+ if (camDeviceId === void 0) {
582707
+ throw new server_1.TRPCError({
582708
+ code: "BAD_REQUEST",
582709
+ message: "deviceAdoption.resync requires camDeviceId (owning addon is resolved from it) or an explicit addonId pin."
582710
+ });
582711
+ }
582712
+ const resolved = deps.resolveDeviceAddonId ? await Promise.resolve(deps.resolveDeviceAddonId(camDeviceId)) : null;
582713
+ if (resolved === null || resolved === "") {
582714
+ throw new server_1.TRPCError({
582715
+ code: "BAD_REQUEST",
582716
+ message: `deviceAdoption.${methodName}: device ${camDeviceId} not found \u2014 pass addonId explicitly.`
582717
+ });
582718
+ }
582719
+ return resolved;
582720
+ }
582721
+ if (integrationId !== void 0) {
582722
+ const resolved = deps.resolveIntegrationAddonId ? await Promise.resolve(deps.resolveIntegrationAddonId(integrationId)) : null;
582723
+ if (resolved === null || resolved === "") {
582724
+ throw new server_1.TRPCError({
582725
+ code: "BAD_REQUEST",
582726
+ message: `deviceAdoption.${methodName}: unknown integrationId "${integrationId}" \u2014 pass addonId explicitly.`
582727
+ });
582728
+ }
582729
+ return resolved;
582730
+ }
582731
+ throw new server_1.TRPCError({
582732
+ code: "BAD_REQUEST",
582733
+ message: `deviceAdoption.${methodName} requires addonId (top-level pin) or integrationId to resolve the owning provider.`
582734
+ });
582735
+ }
581600
582736
  function createCapRouterServices(deps) {
581601
582737
  const reg = deps.capabilityRegistry;
581602
582738
  const remoteProxy = (capName, nodeId) => deps.moleculer.createCapabilityProxy(capName, nodeId);
@@ -581645,10 +582781,16 @@ var require_cap_router_runtime = __commonJS({
581645
582781
  message: `Capability "${capName}" has no provider with addonId "${addonId}". Valid addonId(s): ${known.join(", ")}. An unresolvable addonId is refused rather than answered by another provider.`
581646
582782
  });
581647
582783
  };
582784
+ const inferCollectionAddonPin = (capName, methodName, methodInput) => {
582785
+ if (capName !== "device-adoption")
582786
+ return void 0;
582787
+ return inferDeviceAdoptionCollectionAddonPin(methodName, methodInput, deps);
582788
+ };
581648
582789
  return {
581649
582790
  getLocalProvider,
581650
582791
  remoteProxy,
581651
582792
  rejectUnresolvedAddonPin,
582793
+ inferCollectionAddonPin,
581652
582794
  noProvider: (capName, nodeId) => {
581653
582795
  if (nodeId !== void 0 && nodeId !== "hub") {
581654
582796
  throw new server_1.TRPCError({
@@ -581846,11 +582988,18 @@ var require_trpc_router = __commonJS({
581846
582988
  };
581847
582989
  }
581848
582990
  function buildCapabilityRouters(services) {
582991
+ const integrationRegistry = services.addonRegistry.getIntegrationRegistry();
582992
+ const deviceRegistry = services.addonRegistry.getDeviceRegistry();
581849
582993
  const dynamicCaps = (0, cap_router_runtime_js_1.buildRuntimeCapRouters)({
581850
582994
  capabilityRegistry: services.capabilityRegistry,
581851
582995
  moleculer: services.moleculer,
581852
582996
  serverProviders: buildServerProviders(services),
581853
- logger: services.loggingService.createLogger("cap-router")
582997
+ logger: services.loggingService.createLogger("cap-router"),
582998
+ resolveIntegrationAddonId: integrationRegistry ? async (integrationId) => {
582999
+ const row = await integrationRegistry.getIntegration(integrationId);
583000
+ return row?.addonId ?? null;
583001
+ } : void 0,
583002
+ resolveDeviceAddonId: (camDeviceId) => deviceRegistry.getAddonId(camDeviceId)
581854
583003
  });
581855
583004
  return {
581856
583005
  // ── Runtime-built cap routers: EVERY cap whose mount is
@@ -583353,6 +584502,28 @@ var require_addon_process_view = __commonJS({
583353
584502
  }
583354
584503
  });
583355
584504
 
584505
+ // ../../server/backend/dist/core/addon/hub-addon-api.js
584506
+ var require_hub_addon_api = __commonJS({
584507
+ "../../server/backend/dist/core/addon/hub-addon-api.js"(exports) {
584508
+ "use strict";
584509
+ Object.defineProperty(exports, "__esModule", { value: true });
584510
+ exports.createHubAddonApi = createHubAddonApi;
584511
+ var client_1 = require_dist2();
584512
+ var system_1 = require_dist3();
584513
+ function createHubAddonApi(options) {
584514
+ const observerOpts = {
584515
+ callerAddonId: options.callerAddonId,
584516
+ providerAddonIdForCap: () => null,
584517
+ observer: () => void 0
584518
+ };
584519
+ const client = (0, client_1.createTRPCClient)({
584520
+ links: [(0, system_1.localProviderLink)(options.resolver, observerOpts), ...options.links]
584521
+ });
584522
+ return client;
584523
+ }
584524
+ }
584525
+ });
584526
+
583356
584527
  // ../../server/backend/dist/core/addon/addon-call-gateway.js
583357
584528
  var require_addon_call_gateway = __commonJS({
583358
584529
  "../../server/backend/dist/core/addon/addon-call-gateway.js"(exports) {
@@ -584199,6 +585370,7 @@ var require_addon_registry_service = __commonJS({
584199
585370
  var system_1 = require_dist3();
584200
585371
  var types_1 = require_dist4();
584201
585372
  var client_1 = require_dist2();
585373
+ var hub_addon_api_js_1 = require_hub_addon_api();
584202
585374
  var addon_call_gateway_js_1 = require_addon_call_gateway();
584203
585375
  var addon_row_manifest_1 = require_addon_row_manifest();
584204
585376
  var addon_settings_provider_js_1 = require_addon_settings_provider();
@@ -584409,29 +585581,41 @@ var require_addon_registry_service = __commonJS({
584409
585581
  */
584410
585582
  getBrokerApi() {
584411
585583
  if (!this.brokerApi) {
584412
- const reg = this.capabilityRegistry;
584413
- const resolver = {
584414
- getByName: (capName) => reg.getSingleton(capName) ?? reg.getAllProviders(capName)[0] ?? null
584415
- };
584416
- const client = (0, client_1.createTRPCClient)({
584417
- links: [
584418
- (0, system_1.localProviderLink)(resolver),
584419
- // Device-scoped caps go through the hub's CapRouteResolver, which is
584420
- // the only authority that can route `(cap, deviceId)` into a FORKED
584421
- // child — and forked children are broker-less, so the Moleculer
584422
- // lookup below cannot see them at all. Without this an in-process
584423
- // addon asking `ptz.getOptions({deviceId})` got
584424
- // `Service 'ptz.ptz.getOptions' is not found`, which is how the
584425
- // autotrack builtin came up switched on and unable to move a camera
584426
- // (2026-09-18).
584427
- (0, system_1.deviceCapResolverLink)((capName, method, input) => this.moleculer.callCapabilityOnNode(this.moleculer.broker.nodeID, capName, method, input), isDeviceScopedCapName),
584428
- (0, system_1.brokerTransportLink)(this.moleculer.broker)
584429
- ]
584430
- });
584431
- this.brokerApi = client;
585584
+ this.brokerApi = this.buildBrokerApi(void 0);
584432
585585
  }
584433
585586
  return this.brokerApi;
584434
585587
  }
585588
+ /**
585589
+ * The `ctx.api` of one in-process addon. Same chain as {@link getBrokerApi},
585590
+ * but its `localProviderLink` knows WHO is calling, so a caller-stamped cap
585591
+ * (`device-history`, keychain, records) can identify the addon. A shared
585592
+ * client cannot: `metrics-native` was refused `declareSeries` ("the caller
585593
+ * addon could not be identified") on every sample.
585594
+ */
585595
+ getAddonApi(addonId) {
585596
+ const cached = this.addonApis.get(addonId);
585597
+ if (cached)
585598
+ return cached;
585599
+ const api = this.buildBrokerApi(addonId);
585600
+ this.addonApis.set(addonId, api);
585601
+ return api;
585602
+ }
585603
+ addonApis = /* @__PURE__ */ new Map();
585604
+ buildBrokerApi(callerAddonId) {
585605
+ const reg = this.capabilityRegistry;
585606
+ const resolver = {
585607
+ getByName: (capName) => reg.getSingleton(capName) ?? reg.getAllProviders(capName)[0] ?? null
585608
+ };
585609
+ const links = [
585610
+ (0, system_1.deviceCapResolverLink)((capName, method, input) => this.moleculer.callCapabilityOnNode(this.moleculer.broker.nodeID, capName, method, input), isDeviceScopedCapName),
585611
+ (0, system_1.brokerTransportLink)(this.moleculer.broker)
585612
+ ];
585613
+ if (callerAddonId !== void 0) {
585614
+ return (0, hub_addon_api_js_1.createHubAddonApi)({ resolver, callerAddonId, links });
585615
+ }
585616
+ const client = (0, client_1.createTRPCClient)({ links: [(0, system_1.localProviderLink)(resolver), ...links] });
585617
+ return client;
585618
+ }
584435
585619
  // Common settings resolver (3-level: defaults → global → per-device).
584436
585620
  // Lazily constructed on first call to `getSettingsResolver()` so tests
584437
585621
  // that don't exercise the settings API can instantiate the service
@@ -586418,7 +587602,7 @@ var require_addon_registry_service = __commonJS({
586418
587602
  dataPlane: dataPlaneFacility.dataPlane,
586419
587603
  peerBytes: peerBytesFacility.peerBytes,
586420
587604
  get api() {
586421
- return registry.getBrokerApi();
587605
+ return registry.getAddonApi(addonId);
586422
587606
  },
586423
587607
  integrationRegistry: this.integrationRegistry ?? void 0,
586424
587608
  // Live capability-collection accessor used by addons like stream-broker
@@ -591935,7 +593119,7 @@ var require_manual_boot = __commonJS({
591935
593119
  throw new Error("the process service did not answer");
591936
593120
  return rows;
591937
593121
  },
591938
- // The verdict reaches the alert centre (one alert + the direct critical push).
593122
+ // The verdict reaches the alert centre (one critical alert).
591939
593123
  onResult: (check) => {
591940
593124
  eventBusService.emit({
591941
593125
  id: (0, node_crypto_1.randomUUID)(),