@camstack/addon-auth 1.2.171 → 1.2.172

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.
@@ -5587,7 +5587,7 @@ var ZodIssueCode = {
5587
5587
  var ZodFirstPartyTypeKind;
5588
5588
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5589
5589
  //#endregion
5590
- //#region ../types/dist/sleep-63gM_1JR.mjs
5590
+ //#region ../types/dist/sleep-CeyhrcOk.mjs
5591
5591
  /**
5592
5592
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5593
5593
  * window to float samples (D455).
@@ -7654,6 +7654,7 @@ var BaseAddon = class {
7654
7654
  * a warning and falls back rather than crashing boot.
7655
7655
  */
7656
7656
  state(key, schema, fallback) {
7657
+ this._declaredStateKeys.add(key);
7657
7658
  return createDurableState({
7658
7659
  key,
7659
7660
  schema,
@@ -7669,6 +7670,96 @@ var BaseAddon = class {
7669
7670
  } })
7670
7671
  });
7671
7672
  }
7673
+ /** Durable-state keys this process registered through {@link state}. */
7674
+ _declaredStateKeys = /* @__PURE__ */ new Set();
7675
+ /**
7676
+ * Stale-data audit: every global-settings key this addon still declares —
7677
+ * constructor defaults, settings-schema fields (layout containers included),
7678
+ * durable-state keys and the root of each `secretStatePaths` entry. Answered
7679
+ * here, in the addon's own process, so the hub keeps no copy of it.
7680
+ */
7681
+ async getDeclaredSettingsKeys() {
7682
+ return { keys: [...this.declaredKeySet()].toSorted() };
7683
+ }
7684
+ declaredKeySet() {
7685
+ const keys = new Set(Object.keys(this.defaults));
7686
+ const schema = this.globalSettingsSchema();
7687
+ if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
7688
+ for (const key of this._declaredStateKeys) keys.add(key);
7689
+ for (const path of this.secretStatePaths) {
7690
+ const root = path.split(/[.[]/, 1)[0];
7691
+ if (root) keys.add(root);
7692
+ }
7693
+ return keys;
7694
+ }
7695
+ /**
7696
+ * Stale-data audit: remove stored global-settings keys the addon no longer
7697
+ * declares, and clear the keychain values their markers held — except a value
7698
+ * a surviving key still points at. A declared or absent key is refused.
7699
+ */
7700
+ async unsetStoredSettingsKeys(keys) {
7701
+ const settings = this._ctx?.settings;
7702
+ const raw = this.rawSettings();
7703
+ if (!settings || !raw) return {
7704
+ removed: [],
7705
+ refused: keys.map((key) => ({
7706
+ key,
7707
+ reason: "no settings store"
7708
+ })),
7709
+ secretsCleared: 0
7710
+ };
7711
+ let stored;
7712
+ try {
7713
+ stored = await raw.readAddonStore();
7714
+ } catch (error) {
7715
+ const reason = `store unreadable: ${error instanceof Error ? error.message : "unknown"}`;
7716
+ return {
7717
+ removed: [],
7718
+ refused: keys.map((key) => ({
7719
+ key,
7720
+ reason
7721
+ })),
7722
+ secretsCleared: 0
7723
+ };
7724
+ }
7725
+ const declared = this.declaredKeySet();
7726
+ const removed = [];
7727
+ const refused = [];
7728
+ for (const key of keys) {
7729
+ const base = key.split("@", 1)[0] ?? key;
7730
+ if (declared.has(base)) refused.push({
7731
+ key,
7732
+ reason: "still declared by the addon"
7733
+ });
7734
+ else if (!(key in stored)) refused.push({
7735
+ key,
7736
+ reason: "not stored"
7737
+ });
7738
+ else removed.push(key);
7739
+ }
7740
+ if (removed.length === 0) return {
7741
+ removed,
7742
+ refused,
7743
+ secretsCleared: 0
7744
+ };
7745
+ const kept = /* @__PURE__ */ new Set();
7746
+ const held = /* @__PURE__ */ new Set();
7747
+ for (const [key, value] of Object.entries(stored)) {
7748
+ const target = removed.includes(key) ? held : kept;
7749
+ for (const ref of collectSecretMarkerRefs(value)) target.add(ref);
7750
+ }
7751
+ const refsToClear = [...held].filter((ref) => !kept.has(ref));
7752
+ const patch = {};
7753
+ for (const key of removed) patch[key] = void 0;
7754
+ await settings.writeAddonStore(patch);
7755
+ const secretsCleared = refsToClear.length > 0 && this._ctx?.secrets ? await this._ctx.secrets.delete(refsToClear) : 0;
7756
+ await this.resolveConfig();
7757
+ return {
7758
+ removed,
7759
+ refused,
7760
+ secretsCleared
7761
+ };
7762
+ }
7672
7763
  /** Per-device variant of {@link state}, backed by the per-device store. */
7673
7764
  deviceState(deviceId, key, schema, fallback) {
7674
7765
  return createDurableState({
@@ -7939,6 +8030,21 @@ function dropPerNodeFields(fields) {
7939
8030
  }
7940
8031
  return kept;
7941
8032
  }
8033
+ function collectFieldKeys(fields) {
8034
+ const collected = [];
8035
+ for (const field of fields) {
8036
+ if (field.type === "group") {
8037
+ collected.push(...collectFieldKeys(field.fields));
8038
+ continue;
8039
+ }
8040
+ if (field.type === "sub-tabs") {
8041
+ for (const tab of field.tabs) collected.push(...collectFieldKeys(tab.fields));
8042
+ continue;
8043
+ }
8044
+ if ("key" in field && typeof field.key === "string") collected.push(field.key);
8045
+ }
8046
+ return collected;
8047
+ }
7942
8048
  function collectPerNodeFieldKeys(fields) {
7943
8049
  const collected = [];
7944
8050
  for (const field of fields) {
@@ -16623,8 +16729,172 @@ object({
16623
16729
  quantity: ReadingQuantitySchema,
16624
16730
  unit: string().min(1)
16625
16731
  });
16626
- /** How a leaf compares a device field to a value. Derived from the field's
16627
- * `kind` in `deviceManager.getWireableFields`, never hand-maintained. */
16732
+ new Set([
16733
+ {
16734
+ id: "person",
16735
+ name: "Person"
16736
+ },
16737
+ {
16738
+ id: "vehicle",
16739
+ name: "Vehicle"
16740
+ },
16741
+ {
16742
+ id: "animal",
16743
+ name: "Animal"
16744
+ },
16745
+ {
16746
+ id: "package",
16747
+ name: "Package"
16748
+ }
16749
+ ].map((l) => l.id));
16750
+ var COCO_TO_MACRO = {
16751
+ mapping: {
16752
+ person: "person",
16753
+ bicycle: "vehicle",
16754
+ car: "vehicle",
16755
+ motorcycle: "vehicle",
16756
+ airplane: "vehicle",
16757
+ bus: "vehicle",
16758
+ train: "vehicle",
16759
+ truck: "vehicle",
16760
+ boat: "vehicle",
16761
+ bird: "animal",
16762
+ cat: "animal",
16763
+ dog: "animal",
16764
+ horse: "animal",
16765
+ sheep: "animal",
16766
+ cow: "animal",
16767
+ elephant: "animal",
16768
+ bear: "animal",
16769
+ zebra: "animal",
16770
+ giraffe: "animal",
16771
+ suitcase: "package",
16772
+ backpack: "package",
16773
+ handbag: "package"
16774
+ },
16775
+ preserveOriginal: false
16776
+ };
16777
+ /**
16778
+ * WHAT a detection-shaped leaf is about: one MACRO class (person, vehicle,
16779
+ * animal, package) plus an OPTIONAL label that narrows it.
16780
+ *
16781
+ * The taxonomy is `COCO_TO_MACRO` (and `CLASS_MAP_MACRO_TARGETS`) — never a
16782
+ * second list. The label that fits depends on the macro class:
16783
+ *
16784
+ * - `animal` → a sub-class (`dog`, `cat`, …);
16785
+ * - `person` → a known face (an identity of the `face-gallery`);
16786
+ * - `vehicle` → a plate (its text) or a sub-class (`car`, `truck`, …);
16787
+ * - `package` → a sub-class.
16788
+ *
16789
+ * `other` exists only so a class the taxonomy does not know (a custom model's
16790
+ * `forklift`) survives the conversion of a stored recipe without widening.
16791
+ *
16792
+ * A label is decided LATE (D683): face recognition and LPR land after the
16793
+ * detection starts. The track-lifecycle `update` event carries the newly
16794
+ * resolved `label` / `plateText`, which is why a leaf with a label fires when
16795
+ * the subject GETS it — not at the first detection.
16796
+ */
16797
+ var SubClassLabelSchema = object({
16798
+ kind: literal("sub-class"),
16799
+ className: string().min(1)
16800
+ });
16801
+ var IdentityLabelSchema = object({
16802
+ kind: literal("identity"),
16803
+ /** The `face-gallery` identity id (stable across a rename). */
16804
+ identityId: string().min(1),
16805
+ /** The identity's name — what a track-lifecycle event carries as `label`. */
16806
+ name: string().min(1)
16807
+ });
16808
+ var PlateLabelSchema = object({
16809
+ kind: literal("plate"),
16810
+ text: string().min(1)
16811
+ });
16812
+ function macroOf$1(className) {
16813
+ return COCO_TO_MACRO.mapping[className.toLowerCase()] ?? className.toLowerCase();
16814
+ }
16815
+ /** A sub-class label must belong to the macro it is under. */
16816
+ function subClassBelongs(macro, label) {
16817
+ return label === void 0 || macroOf$1(label.className) === macro;
16818
+ }
16819
+ var subClassOf = (macro) => SubClassLabelSchema.refine((l) => subClassBelongs(macro, l), { message: `that class is not a kind of ${macro} in the detection taxonomy` });
16820
+ var DetectionSubjectSchema = discriminatedUnion("macro", [
16821
+ object({
16822
+ macro: literal("animal"),
16823
+ label: subClassOf("animal").optional()
16824
+ }),
16825
+ object({
16826
+ macro: literal("person"),
16827
+ label: IdentityLabelSchema.optional()
16828
+ }),
16829
+ object({
16830
+ macro: literal("vehicle"),
16831
+ label: discriminatedUnion("kind", [SubClassLabelSchema.refine((l) => subClassBelongs("vehicle", l), { message: "that class is not a kind of vehicle in the detection taxonomy" }), PlateLabelSchema]).optional()
16832
+ }),
16833
+ object({
16834
+ macro: literal("package"),
16835
+ label: subClassOf("package").optional()
16836
+ }),
16837
+ object({
16838
+ macro: literal("other"),
16839
+ className: string().min(1)
16840
+ })
16841
+ ]);
16842
+ /**
16843
+ * The subject of a PARKED object. The stationary registry's event carries a
16844
+ * class and a zone, no face and no plate — an identity or plate label there
16845
+ * could never match, so the schema refuses it instead of letting it sit dead.
16846
+ */
16847
+ var StationarySubjectSchema = DetectionSubjectSchema.refine((s) => s.macro === "other" || s.label === void 0 || s.label.kind === "sub-class", { message: "a parked object carries no face or plate — only a class can narrow it" });
16848
+ /**
16849
+ * A stored class string as a subject: a macro name stays a macro; a raw class
16850
+ * the taxonomy files under a macro becomes that macro plus the sub-class label
16851
+ * (`dog` → animal / dog); anything else is `other`, kept verbatim.
16852
+ */
16853
+ function subjectFromClassName(className) {
16854
+ const name = className.trim().toLowerCase();
16855
+ const macro = COCO_TO_MACRO.mapping[name];
16856
+ if (new Set(Object.values(COCO_TO_MACRO.mapping)).has(name)) return macroOnly(name);
16857
+ if (macro === void 0) return {
16858
+ macro: "other",
16859
+ className
16860
+ };
16861
+ const label = {
16862
+ kind: "sub-class",
16863
+ className: name
16864
+ };
16865
+ switch (macro) {
16866
+ case "animal": return {
16867
+ macro: "animal",
16868
+ label
16869
+ };
16870
+ case "vehicle": return {
16871
+ macro: "vehicle",
16872
+ label
16873
+ };
16874
+ case "package": return {
16875
+ macro: "package",
16876
+ label
16877
+ };
16878
+ default: return {
16879
+ macro: "other",
16880
+ className
16881
+ };
16882
+ }
16883
+ }
16884
+ function macroOnly(macro) {
16885
+ switch (macro) {
16886
+ case "animal": return { macro: "animal" };
16887
+ case "person": return { macro: "person" };
16888
+ case "vehicle": return { macro: "vehicle" };
16889
+ case "package": return { macro: "package" };
16890
+ default: return {
16891
+ macro: "other",
16892
+ className: macro
16893
+ };
16894
+ }
16895
+ }
16896
+ /** How a state leaf compares a device field to a value. Derived from the
16897
+ * field's `kind` in `deviceManager.getWireableFields`, never hand-maintained. */
16628
16898
  var AutomationConditionOperatorSchema = _enum([
16629
16899
  "eq",
16630
16900
  "ne",
@@ -16635,12 +16905,76 @@ var AutomationConditionOperatorSchema = _enum([
16635
16905
  "contains",
16636
16906
  "in"
16637
16907
  ]);
16638
- var AutomationConditionLeafSchema = object({
16639
- kind: literal("condition"),
16908
+ /**
16909
+ * D8 compliance, and it is the reason a "becomes" leaf is not merely an event
16910
+ * subscription: it evaluates against the **state mirror**, which is reconciled,
16911
+ * and an event only WAKES the evaluation. A dropped event therefore DELAYS a
16912
+ * fire; it does not lose it.
16913
+ */
16914
+ var DeviceBecomesLeafSchema = object({
16915
+ kind: literal("device-becomes"),
16916
+ deviceId: number().int().nonnegative(),
16917
+ cap: string().min(1),
16918
+ fieldPath: string().min(1),
16919
+ /** Fire when the field takes this value. Omit to fire on any change. */
16920
+ becomes: union([
16921
+ string(),
16922
+ number(),
16923
+ boolean()
16924
+ ]).optional(),
16925
+ /** Only on a CHANGE of value, not on every re-report. */
16926
+ edge: boolean().optional(),
16927
+ /** The new value must hold this long before it counts. */
16928
+ forMs: number().int().min(0).max(864e5).optional(),
16929
+ /** Collapse a burst into one occurrence. */
16930
+ debounceMs: number().int().min(0).max(6e5).optional()
16931
+ });
16932
+ var DeviceEventLeafSchema = object({
16933
+ kind: literal("device-event"),
16934
+ /** An `EventCategory` value. */
16935
+ category: string().min(1),
16936
+ deviceId: number().int().nonnegative().optional()
16937
+ });
16938
+ var ScheduleLeafSchema = object({
16939
+ kind: literal("schedule"),
16940
+ cron: string().min(1).max(120)
16941
+ });
16942
+ var ManualLeafSchema = object({ kind: literal("manual") });
16943
+ var IntervalLeafSchema = object({
16944
+ kind: literal("interval"),
16945
+ minutes: number().int().min(1).max(1440)
16946
+ });
16947
+ var DetectionLeafSchema = object({
16948
+ kind: literal("detection"),
16949
+ /** One macro class each, optionally narrowed by a label; a track of any listed subject fires. */
16950
+ subjects: array(DetectionSubjectSchema).min(1),
16951
+ deviceIds: array(number().int().nonnegative()).min(1),
16952
+ /** The track must have visited at least one of these zones. */
16953
+ zones: array(string().min(1)).min(1).optional(),
16954
+ /** Best-so-far confidence the track must reach. */
16955
+ minScore: number().min(0).max(1).optional()
16956
+ });
16957
+ var StationaryLeafSchema = object({
16958
+ kind: literal("stationary"),
16959
+ deviceIds: array(number().int().nonnegative()).min(1),
16960
+ subjects: array(StationarySubjectSchema).min(1).optional(),
16961
+ zones: array(string().min(1)).min(1).optional(),
16962
+ /** `appeared`: it became parked. `departed`: the registry retired it. */
16963
+ phase: _enum(["appeared", "departed"])
16964
+ });
16965
+ /** "is": one device field compared to one value, NOW. */
16966
+ var DeviceIsLeafSchema = object({
16967
+ kind: literal("device-is"),
16640
16968
  deviceId: number().int().nonnegative(),
16641
16969
  cap: string().min(1),
16642
16970
  fieldPath: string().min(1),
16643
16971
  operator: AutomationConditionOperatorSchema,
16972
+ /**
16973
+ * The value. A number on a quantity field is stored in the field's base unit
16974
+ * (D944); a quantity value is the WRITE form, which the save boundary
16975
+ * replaces with the converted number and `unit`. A leaf that still carries
16976
+ * one is never evaluated (it fails).
16977
+ */
16644
16978
  value: union([
16645
16979
  string(),
16646
16980
  number(),
@@ -16648,24 +16982,26 @@ var AutomationConditionLeafSchema = object({
16648
16982
  array(union([string(), number()])),
16649
16983
  QuantityValueSchema
16650
16984
  ]),
16985
+ /** The unit the person picked for `value` (display only; `value` is in the base unit). */
16651
16986
  unit: string().min(1).optional()
16652
16987
  });
16653
- var AutomationConditionStationaryPresentSchema = object({
16988
+ /**
16989
+ * True when the camera currently has a PARKED object matching `subjects` in at
16990
+ * least one of `zones`. Reads the zone-analytics snapshot through the same
16991
+ * mirror as every other state leaf, so a dropped event never decides it (D49).
16992
+ */
16993
+ var StationaryPresentLeafSchema = object({
16654
16994
  kind: literal("stationary-present"),
16655
16995
  deviceId: number().int().nonnegative(),
16656
- /** Class names; a macro class matches every raw class that maps to it. */
16657
- classes: array(string().min(1)).min(1).optional(),
16658
- /** The parked object must be in at least one of these zones. */
16996
+ subjects: array(StationarySubjectSchema).min(1).optional(),
16659
16997
  zones: array(string().min(1)).min(1).optional()
16660
16998
  });
16661
16999
  /**
16662
- * The expression leaf, declared as a plain object rather than an intersection
16663
- * with {@link ExpressionSourceSchema}: a discriminated union has to be able to
16664
- * read `kind` off each option, and an intersection hides it. The author-time
16665
- * validation is the SAME function `ExpressionSourceSchema` runs, so the two
16666
- * cannot drift — an expression that one accepts, the other accepts.
17000
+ * The escape hatch: the bounded expression engine over named device bindings.
17001
+ * Declared as a plain object so a discriminated union can read `kind`; the
17002
+ * author-time validation is the SAME function `ExpressionSourceSchema` runs.
16667
17003
  */
16668
- var AutomationConditionExpressionSchema = object({
17004
+ var ExpressionLeafSchema = object({
16669
17005
  kind: literal("expression"),
16670
17006
  expr: string().min(1).max(MAX_EXPRESSION_SOURCE_LENGTH),
16671
17007
  bindings: record(string().regex(EXPRESSION_IDENTIFIER_RE), ExpressionBindingSourceSchema)
@@ -16677,55 +17013,62 @@ var AutomationConditionExpressionSchema = object({
16677
17013
  path: ["expr"]
16678
17014
  });
16679
17015
  });
16680
- var AutomationConditionSchema = lazy(() => discriminatedUnion("kind", [
17016
+ var AutomationWhenSchema = lazy(() => discriminatedUnion("kind", [
16681
17017
  object({
16682
17018
  kind: literal("all"),
16683
- children: array(AutomationConditionSchema)
17019
+ children: array(AutomationWhenSchema)
16684
17020
  }),
16685
17021
  object({
16686
17022
  kind: literal("any"),
16687
- children: array(AutomationConditionSchema)
17023
+ children: array(AutomationWhenSchema)
16688
17024
  }),
16689
17025
  object({
16690
17026
  kind: literal("not"),
16691
- child: AutomationConditionSchema
17027
+ child: AutomationWhenSchema
16692
17028
  }),
16693
- AutomationConditionLeafSchema,
16694
- AutomationConditionExpressionSchema,
16695
- AutomationConditionStationaryPresentSchema
17029
+ DeviceBecomesLeafSchema,
17030
+ DeviceEventLeafSchema,
17031
+ ScheduleLeafSchema,
17032
+ ManualLeafSchema,
17033
+ IntervalLeafSchema,
17034
+ DetectionLeafSchema,
17035
+ StationaryLeafSchema,
17036
+ DeviceIsLeafSchema,
17037
+ StationaryPresentLeafSchema,
17038
+ ExpressionLeafSchema
16696
17039
  ]));
16697
17040
  /**
16698
- * What starts a run.
16699
- *
16700
- * D8 compliance, and it is the reason `device-state` is not merely an event
16701
- * subscription: the trigger evaluates against the **state mirror**, which is
16702
- * reconciled, and an event only WAKES the evaluation. A dropped event therefore
16703
- * DELAYS a trigger; it does not lose it. `schedule` uses `croner` — the one
16704
- * already in the repo — because `setInterval(24h)` drifts and "at 23:30" does
16705
- * not.
16706
- */
16707
- var AutomationTriggerSchema = discriminatedUnion("kind", [
17041
+ * The recipe shape before the single `when` tree: a flat list of triggers
17042
+ * (OR'd, each an instant) and an optional condition tree. Stored recipes —
17043
+ * the `AUTOMATION_RECIPE` literal of every block saved so far, and whatever an
17044
+ * older client still posts — are in this shape, so it stays READABLE here, and
17045
+ * {@link convertLegacyRecipe} turns it into the new one, losslessly and one
17046
+ * way. Nothing writes this shape any more.
17047
+ *
17048
+ * `{ triggers: [t1…tn], conditions?: C }`
17049
+ * → `when = all(any(t1…tn), C)`, or `any(t1…tn)` without `C`;
17050
+ * a single trigger is not wrapped in `any`.
17051
+ * a `condition` leaf → a `device-is` leaf (the "is" verb);
17052
+ * a `device-state` trigger → a `device-becomes` leaf (the "becomes" verb);
17053
+ * `classes: ['dog']` → the macro `animal` with the label `dog`.
17054
+ */
17055
+ var LegacyTriggerSchema = discriminatedUnion("kind", [
16708
17056
  object({
16709
17057
  kind: literal("device-state"),
16710
17058
  deviceId: number().int().nonnegative(),
16711
17059
  cap: string().min(1),
16712
17060
  fieldPath: string().min(1),
16713
- /** Fire when the field takes this value. Omit to fire on any change. */
16714
17061
  becomes: union([
16715
17062
  string(),
16716
17063
  number(),
16717
17064
  boolean()
16718
17065
  ]).optional(),
16719
- /** Only on a CHANGE of value, not on every re-report. */
16720
17066
  edge: boolean().optional(),
16721
- /** The condition must hold this long before the run starts. */
16722
17067
  forMs: number().int().min(0).max(864e5).optional(),
16723
- /** Collapse a burst into one run. */
16724
17068
  debounceMs: number().int().min(0).max(6e5).optional()
16725
17069
  }),
16726
17070
  object({
16727
17071
  kind: literal("device-event"),
16728
- /** An `EventCategory` value. */
16729
17072
  category: string().min(1),
16730
17073
  deviceId: number().int().nonnegative().optional()
16731
17074
  }),
@@ -16736,25 +17079,16 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
16736
17079
  object({ kind: literal("manual") }),
16737
17080
  object({
16738
17081
  kind: literal("detection"),
16739
- /** Class names. A macro class (`animal`, `vehicle`, `person`, `package`)
16740
- * matches every raw class that maps to it; any other name matches
16741
- * exactly. */
16742
17082
  classes: array(string().min(1)).min(1),
16743
17083
  deviceIds: array(number().int().nonnegative()).min(1),
16744
- /** The track must have visited at least one of these zones. */
16745
17084
  zones: array(string().min(1)).min(1).optional(),
16746
- /** Best-so-far confidence the track must reach. */
16747
17085
  minScore: number().min(0).max(1).optional()
16748
17086
  }),
16749
17087
  object({
16750
17088
  kind: literal("stationary"),
16751
17089
  deviceIds: array(number().int().nonnegative()).min(1),
16752
- /** Class names; a macro class matches like the detection trigger. */
16753
17090
  classes: array(string().min(1)).min(1).optional(),
16754
- /** The parked object must be in at least one of these zones. */
16755
17091
  zones: array(string().min(1)).min(1).optional(),
16756
- /** `appeared`: it became parked. `departed`: the registry retired it.
16757
- * The vocabulary of `PipelineAnalyticsStationaryChanged`. */
16758
17092
  phase: _enum(["appeared", "departed"])
16759
17093
  }),
16760
17094
  object({
@@ -16762,6 +17096,143 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
16762
17096
  minutes: number().int().min(1).max(1440)
16763
17097
  })
16764
17098
  ]);
17099
+ var LegacyConditionLeafSchema = object({
17100
+ kind: literal("condition"),
17101
+ deviceId: number().int().nonnegative(),
17102
+ cap: string().min(1),
17103
+ fieldPath: string().min(1),
17104
+ operator: AutomationConditionOperatorSchema,
17105
+ value: union([
17106
+ string(),
17107
+ number(),
17108
+ boolean(),
17109
+ array(union([string(), number()])),
17110
+ QuantityValueSchema
17111
+ ]),
17112
+ unit: string().min(1).optional()
17113
+ });
17114
+ var LegacyStationaryPresentSchema = object({
17115
+ kind: literal("stationary-present"),
17116
+ deviceId: number().int().nonnegative(),
17117
+ classes: array(string().min(1)).min(1).optional(),
17118
+ zones: array(string().min(1)).min(1).optional()
17119
+ });
17120
+ var LegacyExpressionSchema = object({
17121
+ kind: literal("expression"),
17122
+ expr: string().min(1).max(MAX_EXPRESSION_SOURCE_LENGTH),
17123
+ bindings: record(string().regex(EXPRESSION_IDENTIFIER_RE), ExpressionBindingSourceSchema)
17124
+ }).superRefine((src, ctx) => {
17125
+ const err = validateExpressionSource(src);
17126
+ if (err !== null) ctx.addIssue({
17127
+ code: "custom",
17128
+ message: err,
17129
+ path: ["expr"]
17130
+ });
17131
+ });
17132
+ var LegacyConditionSchema = lazy(() => discriminatedUnion("kind", [
17133
+ object({
17134
+ kind: literal("all"),
17135
+ children: array(LegacyConditionSchema)
17136
+ }),
17137
+ object({
17138
+ kind: literal("any"),
17139
+ children: array(LegacyConditionSchema)
17140
+ }),
17141
+ object({
17142
+ kind: literal("not"),
17143
+ child: LegacyConditionSchema
17144
+ }),
17145
+ LegacyConditionLeafSchema,
17146
+ LegacyExpressionSchema,
17147
+ LegacyStationaryPresentSchema
17148
+ ]));
17149
+ function convertTrigger(trigger) {
17150
+ switch (trigger.kind) {
17151
+ case "device-state": return {
17152
+ ...trigger,
17153
+ kind: "device-becomes"
17154
+ };
17155
+ case "detection": {
17156
+ const { classes, ...rest } = trigger;
17157
+ return {
17158
+ ...rest,
17159
+ subjects: classes.map(subjectFromClassName)
17160
+ };
17161
+ }
17162
+ case "stationary": {
17163
+ const { classes, ...rest } = trigger;
17164
+ return {
17165
+ ...rest,
17166
+ ...classes === void 0 ? {} : { subjects: classes.map(subjectFromClassName) }
17167
+ };
17168
+ }
17169
+ default: return trigger;
17170
+ }
17171
+ }
17172
+ /** An old condition tree as a `when` subtree: `condition` → "is", classes → subjects. */
17173
+ function convertLegacyCondition(condition) {
17174
+ switch (condition.kind) {
17175
+ case "condition": return {
17176
+ ...condition,
17177
+ kind: "device-is"
17178
+ };
17179
+ case "stationary-present": {
17180
+ const { classes, ...rest } = condition;
17181
+ return {
17182
+ ...rest,
17183
+ ...classes === void 0 ? {} : { subjects: classes.map(subjectFromClassName) }
17184
+ };
17185
+ }
17186
+ case "expression": return condition;
17187
+ case "not": return {
17188
+ kind: "not",
17189
+ child: convertLegacyCondition(condition.child)
17190
+ };
17191
+ case "all":
17192
+ case "any": return {
17193
+ kind: condition.kind,
17194
+ children: condition.children.map(convertLegacyCondition)
17195
+ };
17196
+ }
17197
+ }
17198
+ /** `all(any(t1…tn), C)`, or `any(t1…tn)`; a single trigger is not wrapped in `any`. */
17199
+ function convertLegacyWhen(half) {
17200
+ const leaves = half.triggers.map(convertTrigger);
17201
+ const first = leaves[0];
17202
+ const triggers = leaves.length === 1 && first !== void 0 ? first : {
17203
+ kind: "any",
17204
+ children: leaves
17205
+ };
17206
+ if (half.conditions === void 0) return triggers;
17207
+ return {
17208
+ kind: "all",
17209
+ children: [triggers, convertLegacyCondition(half.conditions)]
17210
+ };
17211
+ }
17212
+ /**
17213
+ * The automation RECIPE — what a visually-authored automation is, before it
17214
+ * becomes code.
17215
+ *
17216
+ * This is the data half of `docs/design/2026-08-08-automation-scripts-custom-devices.md`
17217
+ * Phase 6. A recipe is not an engine and never evaluates anything: it is a
17218
+ * declarative description that {@link generateAutomationBlock} compiles to the
17219
+ * TypeScript of an ordinary core block. There is therefore no second rule
17220
+ * engine, no second store and no second runtime — the block runner is the
17221
+ * runtime, `core_blocks` is the store (§2.1).
17222
+ *
17223
+ * Two parts:
17224
+ *
17225
+ * - **`when`** — ONE tree (`./when.ts`) of all/any/not over event leaves
17226
+ * (instants) and state leaves (durations). It replaces the flat trigger list
17227
+ * plus the separate condition tree; the firing rule is in that file and in
17228
+ * ADR "one tree".
17229
+ * - **Actions** — `NcRuleActionSchema`'s two steps, plus the `{kind:'code'}`
17230
+ * fragment of §3.2.2. See {@link AutomationActionSchema} for the ONE place
17231
+ * this had to diverge from "verbatim", and why.
17232
+ *
17233
+ * A recipe stored in the old shape (`triggers` + `conditions`) still LOADS:
17234
+ * {@link AutomationRecipeSchema} (the parse-anywhere schema) converts it on parse. Nothing writes it.
17235
+ */
16765
17236
  /**
16766
17237
  * One action step.
16767
17238
  *
@@ -16864,69 +17335,49 @@ var AutomationRunModeSchema = discriminatedUnion("mode", [
16864
17335
  minutes: number().int().min(1).max(1440)
16865
17336
  })
16866
17337
  ]);
16867
- var AutomationRecipeSchema = object({
16868
- triggers: array(AutomationTriggerSchema),
16869
- conditions: AutomationConditionSchema.optional(),
17338
+ var CurrentAutomationRecipeSchema = object({
17339
+ when: AutomationWhenSchema,
16870
17340
  actions: array(AutomationActionSchema),
16871
17341
  runMode: AutomationRunModeSchema.optional(),
16872
17342
  /** How many run traces the automation keeps (default {@link DEFAULT_TRACE_KEEP}). */
16873
17343
  keepTraces: number().int().min(1).max(100).optional()
16874
17344
  });
16875
- new Set([
16876
- {
16877
- id: "person",
16878
- name: "Person"
16879
- },
16880
- {
16881
- id: "vehicle",
16882
- name: "Vehicle"
16883
- },
16884
- {
16885
- id: "animal",
16886
- name: "Animal"
16887
- },
16888
- {
16889
- id: "package",
16890
- name: "Package"
16891
- }
16892
- ].map((l) => l.id));
16893
- var COCO_TO_MACRO = {
16894
- mapping: {
16895
- person: "person",
16896
- bicycle: "vehicle",
16897
- car: "vehicle",
16898
- motorcycle: "vehicle",
16899
- airplane: "vehicle",
16900
- bus: "vehicle",
16901
- train: "vehicle",
16902
- truck: "vehicle",
16903
- boat: "vehicle",
16904
- bird: "animal",
16905
- cat: "animal",
16906
- dog: "animal",
16907
- horse: "animal",
16908
- sheep: "animal",
16909
- cow: "animal",
16910
- elephant: "animal",
16911
- bear: "animal",
16912
- zebra: "animal",
16913
- giraffe: "animal",
16914
- suitcase: "package",
16915
- backpack: "package",
16916
- handbag: "package"
16917
- },
16918
- preserveOriginal: false
16919
- };
17345
+ /** A recipe as it was stored before the single tree. Read-only: nothing writes this. */
17346
+ var LegacyAutomationRecipeSchema = object({
17347
+ triggers: array(LegacyTriggerSchema),
17348
+ conditions: LegacyConditionSchema.optional(),
17349
+ actions: array(AutomationActionSchema),
17350
+ runMode: AutomationRunModeSchema.optional(),
17351
+ keepTraces: number().int().min(1).max(100).optional()
17352
+ });
17353
+ /** The old shape as the new one, lossless. */
17354
+ function convertLegacyRecipe(legacy) {
17355
+ const { triggers, conditions, ...rest } = legacy;
17356
+ return {
17357
+ when: convertLegacyWhen({
17358
+ triggers,
17359
+ conditions
17360
+ }),
17361
+ ...rest
17362
+ };
17363
+ }
17364
+ /**
17365
+ * Where recipes are PARSED (the core-blocks cap, the MCP tool, the literal a
17366
+ * saved block carries): the new shape as it is, or the old shape converted to
17367
+ * it. The output type is always the new one.
17368
+ */
17369
+ var AutomationRecipeSchema = union([CurrentAutomationRecipeSchema, LegacyAutomationRecipeSchema.transform(convertLegacyRecipe)]);
16920
17370
  new Set(Object.values(COCO_TO_MACRO.mapping));
16921
17371
  /**
16922
17372
  * Run traces — what an automation (or, later, an Action run) DID, node by node,
16923
17373
  * so the editor can paint every block green or red with its reason.
16924
17374
  *
16925
- * A trace is data, not a log: it covers the WHOLE tree — the trigger stage,
16926
- * every condition node (containers and leaves, including the ones a
16927
- * short-circuit never reached), the run-mode verdict and every action step —
16928
- * each addressed by a stable path (`conditions.0.1`, `actions.2`, or `steps.N`
16929
- * for an Action run). Skipped
17375
+ * A trace is data, not a log: it covers the WHOLE tree — the event leaf that
17376
+ * fired, every node of the `when` tree (containers and leaves, including the
17377
+ * ones a short-circuit never reached), the run-mode verdict and every action
17378
+ * step — each addressed by a stable path (`when.0.1`, `actions.2`, or `steps.N`
17379
+ * for an Action run). Traces written before the single tree carry
17380
+ * `conditions.*` paths and still parse. Skipped
16930
17381
  * evaluations are traced too, and identical consecutive skips collapse into a
16931
17382
  * repeat count so a trigger that fails every 5 minutes cannot flush real runs.
16932
17383
  *
@@ -16958,6 +17409,8 @@ var TraceNodeOutcomeSchema = _enum([
16958
17409
  ]);
16959
17410
  var AutomationTriggerTraceSchema = object({
16960
17411
  kind: string().min(1),
17412
+ /** The event leaf that fired (`when`, `when.0.1`); absent on traces written before the single tree. */
17413
+ path: string().min(1).optional(),
16961
17414
  outcome: _enum(["pass", "fail"]),
16962
17415
  /** Why an event was rejected ("class person not in [dog]") or what fired. */
16963
17416
  reason: string(),
@@ -16965,15 +17418,23 @@ var AutomationTriggerTraceSchema = object({
16965
17418
  vars: record(string(), TraceValueSchema)
16966
17419
  });
16967
17420
  var AutomationConditionTraceNodeSchema = object({
16968
- /** `conditions` for the root, `conditions.<i>` for its i-th child, … */
17421
+ /** `when` for the root, `when.<i>` for its i-th child, … (`conditions…` on an old trace). */
16969
17422
  path: string().min(1),
16970
17423
  kind: _enum([
16971
17424
  "all",
16972
17425
  "any",
16973
17426
  "not",
16974
17427
  "condition",
17428
+ "device-is",
16975
17429
  "expression",
16976
- "stationary-present"
17430
+ "stationary-present",
17431
+ "device-becomes",
17432
+ "device-event",
17433
+ "schedule",
17434
+ "interval",
17435
+ "manual",
17436
+ "detection",
17437
+ "stationary"
16977
17438
  ]),
16978
17439
  outcome: TraceNodeOutcomeSchema,
16979
17440
  reason: string(),
@@ -17007,6 +17468,12 @@ var AutomationStepTraceSchema = object({
17007
17468
  deviceId: number().int().optional(),
17008
17469
  startedAt: number().optional(),
17009
17470
  endedAt: number().optional(),
17471
+ /**
17472
+ * The parameters the step SENT, after `{{vars.x}}` substitution — what the
17473
+ * device was actually asked. Bounded like trigger `vars`; a secret parameter
17474
+ * is shown as {@link REDACTED_ARG}. Absent on traces written before it existed.
17475
+ */
17476
+ args: record(string(), TraceValueSchema).optional(),
17010
17477
  outcome: _enum([
17011
17478
  "ok",
17012
17479
  "error",
@@ -17775,6 +18242,23 @@ method(object({
17775
18242
  addonId: string(),
17776
18243
  nodeId: string().optional()
17777
18244
  }), SettingsSchemaWithValuesSchema.nullable()), method(object({
18245
+ addonId: string(),
18246
+ nodeId: string().optional()
18247
+ }), object({ keys: array(string()) }).nullable(), { auth: "admin" }), method(object({
18248
+ addonId: string(),
18249
+ nodeId: string().optional(),
18250
+ keys: array(string())
18251
+ }), object({
18252
+ removed: array(string()),
18253
+ refused: array(object({
18254
+ key: string(),
18255
+ reason: string()
18256
+ })),
18257
+ secretsCleared: number()
18258
+ }), {
18259
+ kind: "mutation",
18260
+ auth: "admin"
18261
+ }), method(object({
17778
18262
  addonId: string(),
17779
18263
  deviceId: number(),
17780
18264
  nodeId: string().optional()
@@ -21730,6 +22214,8 @@ var StorageKindSchema = object({
21730
22214
  "orphaned",
21731
22215
  "dropped"
21732
22216
  ]),
22217
+ /** An owner declared this kind since the engine started. */
22218
+ declaredThisBoot: boolean(),
21733
22219
  /** `null` when the caller asked for no counts (`counts: false`). */
21734
22220
  rows: number().int().nonnegative().nullable(),
21735
22221
  /** Rows x the average size of a sample; null for an empty kind. */
@@ -21741,6 +22227,51 @@ var StorageKindSchema = object({
21741
22227
  /** The whole declaration, secret paths and entry descriptor included (the entries service builds its handle from it). */
21742
22228
  declaration: RecordKindDeclarationSchema
21743
22229
  });
22230
+ _enum([
22231
+ "orphaned",
22232
+ "stale",
22233
+ "unknown"
22234
+ ]);
22235
+ /** A record kind nobody declared since its owner started, or whose owner is gone. */
22236
+ var StaleKindFindingSchema = object({
22237
+ owner: string(),
22238
+ kind: string(),
22239
+ /** `unknown`: the owner could not be judged (not running, or its state unreadable) - never dropped. */
22240
+ verdict: _enum(["orphaned", "unknown"]),
22241
+ reason: string(),
22242
+ rows: number().int().nonnegative().nullable(),
22243
+ /** local-auth's tables are never dropped by this audit. */
22244
+ protected: boolean()
22245
+ });
22246
+ /** A stored global-settings key the running addon no longer declares. */
22247
+ var StaleSettingsKeyFindingSchema = object({
22248
+ addonId: string(),
22249
+ /** The stored key as written, a per-node `<key>@<nodeId>` suffix included. */
22250
+ key: string(),
22251
+ /**
22252
+ * Keychain refs whose ONLY marker sits under this key: in use by stale data,
22253
+ * not orphans yet. Removing the key releases them.
22254
+ */
22255
+ secretRefs: array(string())
22256
+ });
22257
+ /** An addon the audit could not judge, with the reason. Never reported as stale. */
22258
+ var StaleUnknownSchema = object({
22259
+ subject: string(),
22260
+ reason: string()
22261
+ });
22262
+ var StaleDataReportSchema = object({
22263
+ /** Epoch ms of the last finished scan; `null` before the first. */
22264
+ scannedAt: number().nullable(),
22265
+ scanning: boolean(),
22266
+ trigger: _enum([
22267
+ "boot",
22268
+ "interval",
22269
+ "manual"
22270
+ ]).nullable(),
22271
+ kinds: array(StaleKindFindingSchema),
22272
+ settingsKeys: array(StaleSettingsKeyFindingSchema),
22273
+ unknown: array(StaleUnknownSchema)
22274
+ });
21744
22275
  method(object({ ownerPrefix: string().min(1).optional() }), object({ tables: array(StorageTableSchema) }), { auth: "admin" }), method(object({
21745
22276
  owner: string().min(1).optional(),
21746
22277
  /** `false` skips the per-kind row count (a catalog read only). Default true. */
@@ -21748,7 +22279,30 @@ method(object({ ownerPrefix: string().min(1).optional() }), object({ tables: arr
21748
22279
  }), object({ kinds: array(StorageKindSchema) }), { auth: "admin" }), method(RecordQueryInputSchema.extend({
21749
22280
  owner: string().min(1),
21750
22281
  kind: RecordKindNameSchema
21751
- }), RecordQueryResultSchema, { auth: "admin" });
22282
+ }), RecordQueryResultSchema, { auth: "admin" }), method(object({}), StaleDataReportSchema, { auth: "admin" }), method(object({}), StaleDataReportSchema, {
22283
+ kind: "mutation",
22284
+ auth: "admin"
22285
+ }), method(object({
22286
+ owner: string().min(1),
22287
+ kind: RecordKindNameSchema
22288
+ }), object({
22289
+ deleted: number().int().nonnegative(),
22290
+ secretsReleased: number().int().nonnegative()
22291
+ }), {
22292
+ kind: "mutation",
22293
+ auth: "admin",
22294
+ caller: "required"
22295
+ }), method(object({
22296
+ addonId: string().min(1),
22297
+ key: string().min(1)
22298
+ }), object({
22299
+ removed: boolean(),
22300
+ secretsCleared: number().int().nonnegative()
22301
+ }), {
22302
+ kind: "mutation",
22303
+ auth: "admin",
22304
+ caller: "required"
22305
+ });
21752
22306
  /**
21753
22307
  * record-entries - the ONE admin surface for every list of user-created entries
21754
22308
  * (D881): notification targets, brokers, LLM profiles, anything an addon
@@ -21887,6 +22441,12 @@ method(object({
21887
22441
  }), method(object({ deviceId: number().int() }), object({ refs: array(string()).readonly() }), {
21888
22442
  kind: "mutation",
21889
22443
  auth: "admin"
22444
+ }), method(object({
22445
+ refs: RefListSchema,
22446
+ onBehalfOf: string().min(1)
22447
+ }), object({ released: number().int() }), {
22448
+ kind: "mutation",
22449
+ auth: "admin"
21890
22450
  }), method(object({}), array(KeychainSecretMetaSchema).readonly(), { auth: "admin" }), method(object({}), KeychainStateSchema, { auth: "admin" });
21891
22451
  /**
21892
22452
  * device-history — the ONE mechanism for numeric history (D850).
@@ -22676,7 +23236,9 @@ var DiscoveredChildDeviceSchema = lazy(() => object({
22676
23236
  /** Upstream-system identity + rendering hints for the candidate. */
22677
23237
  sourceInfo: SourceInfoSchema.optional(),
22678
23238
  /** Nested entity-children for accordion preview — integrations populate, Hubs omit. */
22679
- children: array(DiscoveredChildDeviceSchema).readonly().optional()
23239
+ children: array(DiscoveredChildDeviceSchema).readonly().optional(),
23240
+ /** `childNativeId` of the gateway candidate in the same list this device sits behind. */
23241
+ parentStableId: string().optional()
22680
23242
  }));
22681
23243
  /**
22682
23244
  * Status block — populated by the integration after each
@@ -43009,6 +43571,79 @@ object({
43009
43571
  metadata: record(string(), unknown()).optional()
43010
43572
  });
43011
43573
  /**
43574
+ * HOW a provider can be found on the network. The provider only DECLARES the
43575
+ * mode; the `local-network` scan engine runs it once for every provider that
43576
+ * asked (one multicast browse, one host sweep) and hands the raw hits back to
43577
+ * `classifyDiscoveryHits`. A provider never opens a discovery socket itself,
43578
+ * except through a `custom` mode.
43579
+ */
43580
+ var DiscoveryModeSchema = discriminatedUnion("kind", [
43581
+ object({
43582
+ kind: literal("mdns"),
43583
+ /** DNS-SD service type, e.g. `_esphomelib._tcp`. */
43584
+ serviceType: string().min(1),
43585
+ /** Regular expression an instance name must match (case-insensitive). */
43586
+ instancePattern: string().optional()
43587
+ }),
43588
+ object({
43589
+ kind: literal("ws-discovery"),
43590
+ /** Probe types (e.g. `dn:NetworkVideoTransmitter`); omitted = any device. */
43591
+ types: array(string()).optional()
43592
+ }),
43593
+ object({
43594
+ kind: literal("ssdp"),
43595
+ searchTarget: string().min(1)
43596
+ }),
43597
+ object({
43598
+ kind: literal("udp-broadcast"),
43599
+ port: number().int().min(1).max(65535),
43600
+ payloadHex: string().regex(/^([0-9a-fA-F]{2})*$/),
43601
+ /** Port replies arrive on, when the device answers somewhere other than the source port. */
43602
+ listenPort: number().int().min(1).max(65535).optional()
43603
+ }),
43604
+ object({
43605
+ kind: literal("http-probe"),
43606
+ port: number().int().min(1).max(65535),
43607
+ path: string().startsWith("/"),
43608
+ method: _enum(["GET", "POST"]).default("GET"),
43609
+ bodyText: string().optional(),
43610
+ contentType: string().optional(),
43611
+ tls: boolean().default(false),
43612
+ /** Status codes that count as a hit. Omitted = any HTTP answer. */
43613
+ acceptStatus: array(number().int()).optional()
43614
+ }),
43615
+ object({
43616
+ kind: literal("custom"),
43617
+ /** Why the engine cannot do it (an allowlisted exception, e.g. a vendor UDP protocol). */
43618
+ reason: string().min(1)
43619
+ })
43620
+ ]);
43621
+ /** What the engine saw on the wire for ONE declared mode; the provider classifies it. */
43622
+ var DiscoveryHitSchema = object({
43623
+ /** Index into the provider's own `getDiscoveryModes()` array. */
43624
+ modeIndex: number().int().min(0),
43625
+ host: string(),
43626
+ port: number().int(),
43627
+ http: object({
43628
+ status: number().int(),
43629
+ headers: record(string(), string()),
43630
+ /** At most 16 KiB. */
43631
+ bodyText: string()
43632
+ }).optional(),
43633
+ mdns: object({
43634
+ instance: string(),
43635
+ host: string(),
43636
+ port: number().int(),
43637
+ txt: record(string(), string())
43638
+ }).optional(),
43639
+ udp: object({ payloadHex: string() }).optional(),
43640
+ wsd: object({
43641
+ xaddrs: array(string()),
43642
+ types: array(string()),
43643
+ scopes: array(string())
43644
+ }).optional()
43645
+ });
43646
+ /**
43012
43647
  * Candidate handed back from discovery and accepted by
43013
43648
  * `adoptDiscoveredDevice`. Shape mirrors the in-process
43014
43649
  * `DiscoveredDevice` interface used by `DeviceDiscovery`.
@@ -43043,7 +43678,17 @@ var DiscoveryCandidateSchema = object({
43043
43678
  /** Numeric id of the device this candidate was matched to. Set with `alreadyOnboarded`. */
43044
43679
  onboardedDeviceId: number().optional(),
43045
43680
  /** Operator-facing name of the matched device, so the UI can say WHICH one it is. */
43046
- onboardedName: string().optional()
43681
+ onboardedName: string().optional(),
43682
+ /**
43683
+ * This candidate is an INTEGRATION to set up (a gateway, a hub, a controller),
43684
+ * not a device to adopt. Replaces the `prefilledConfig.setupIntegration` marker,
43685
+ * which is still read for one release.
43686
+ */
43687
+ setupIntegration: boolean().optional(),
43688
+ /** `stableId` of the gateway candidate this device was found BEHIND. */
43689
+ parentStableId: string().optional(),
43690
+ /** `false` = shown for context but not pickable. Default true. */
43691
+ selectable: boolean().optional()
43047
43692
  });
43048
43693
  /**
43049
43694
  * Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
@@ -43096,6 +43741,12 @@ method(_void(), _void(), { kind: "mutation" }), method(_void(), _void(), { kind:
43096
43741
  }), method(object({}), boolean()), method(object({ params: record(string(), unknown()).optional() }), array(DiscoveryCandidateSchema), {
43097
43742
  kind: "mutation",
43098
43743
  auth: "admin"
43744
+ }), method(object({}), array(DiscoveryModeSchema)), method(object({
43745
+ hits: array(DiscoveryHitSchema),
43746
+ params: record(string(), unknown()).optional()
43747
+ }), array(DiscoveryCandidateSchema), {
43748
+ kind: "mutation",
43749
+ auth: "admin"
43099
43750
  }), method(object({}), CreationSchemaOutputSchema), method(object({}), object({ deviceType: _enum(DeviceType).nullable() })), method(object({ candidate: DiscoveryCandidateSchema }), DeviceSummarySchema, {
43100
43751
  kind: "mutation",
43101
43752
  auth: "admin"
@@ -43823,17 +44474,23 @@ method(object({
43823
44474
  }), method(object({}), object({ providers: array(object({
43824
44475
  addonId: string(),
43825
44476
  label: string()
43826
- })).readonly() }), { auth: "admin" }), method(object({}), object({ groups: array(object({
43827
- addonId: string(),
43828
- label: string(),
43829
- candidates: array(DiscoveryCandidateSchema).readonly(),
43830
- error: string().nullable()
43831
- })).readonly() }), {
43832
- kind: "mutation",
43833
- auth: "admin"
43834
- }), method(object({
44477
+ })).readonly() }), { auth: "admin" }), method(
44478
+ /** `subnets`: extra CIDRs for THIS scan, on top of the hub's own and the saved list. */
44479
+ object({ subnets: array(string()).optional() }),
44480
+ object({ groups: array(object({
44481
+ addonId: string(),
44482
+ label: string(),
44483
+ candidates: array(DiscoveryCandidateSchema).readonly(),
44484
+ error: string().nullable()
44485
+ })).readonly() }),
44486
+ {
44487
+ kind: "mutation",
44488
+ auth: "admin"
44489
+ }
44490
+ ), method(object({
43835
44491
  addonId: string(),
43836
- params: record(string(), unknown()).optional()
44492
+ params: record(string(), unknown()).optional(),
44493
+ subnets: array(string()).optional()
43837
44494
  }), object({ candidates: array(DiscoveryCandidateSchema).readonly() }), {
43838
44495
  kind: "mutation",
43839
44496
  auth: "admin"
@@ -61512,7 +62169,9 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
61512
62169
  id: string(),
61513
62170
  settings: record(string(), unknown()),
61514
62171
  /** Keys the form's schema declares secret (by type) — keychain-stored (D787). */
61515
- secretKeys: array(string()).optional()
62172
+ secretKeys: array(string()).optional(),
62173
+ /** The integration's own addon applies the change in-process and asks the hub not to respawn it. */
62174
+ skipRestart: boolean().optional()
61516
62175
  }), object({ success: boolean() }), {
61517
62176
  kind: "mutation",
61518
62177
  auth: "admin"
@@ -61989,6 +62648,39 @@ var TlsStatusSchema = object({
61989
62648
  hasCa: boolean()
61990
62649
  }).nullable()
61991
62650
  });
62651
+ /** One provider's ask of the scan engine: the modes it declared. */
62652
+ var ScanRequestSchema = object({
62653
+ providerId: string().min(1),
62654
+ modes: array(DiscoveryModeSchema)
62655
+ });
62656
+ var ScanErrorSchema = object({
62657
+ /** Absent for a failure that is the scan's own (a bad subnet, a socket that would not open). */
62658
+ providerId: string().optional(),
62659
+ /** The mode family that failed (`mdns`, `http-probe`, …). */
62660
+ mode: string().optional(),
62661
+ message: string()
62662
+ });
62663
+ /** What one scan did, per mode family; the engine logs the same numbers once. */
62664
+ var ScanStatsSchema = object({
62665
+ hostsProbed: number().int(),
62666
+ hits: number().int(),
62667
+ timeouts: number().int(),
62668
+ byMode: record(string(), object({
62669
+ probed: number().int(),
62670
+ hits: number().int(),
62671
+ timeouts: number().int()
62672
+ }))
62673
+ });
62674
+ var ScanNetworkResultSchema = object({
62675
+ hits: array(object({
62676
+ providerId: string(),
62677
+ hit: DiscoveryHitSchema
62678
+ })),
62679
+ /** CIDRs actually covered: the hub's own /24s, the saved list and the per-call list. */
62680
+ subnetsScanned: array(string()),
62681
+ errors: array(ScanErrorSchema),
62682
+ stats: ScanStatsSchema
62683
+ });
61992
62684
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
61993
62685
  /**
61994
62686
  * LEGACY HINT — do not send from new code. Kept optional so clients
@@ -62043,6 +62735,16 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
62043
62735
  }), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
62044
62736
  kind: "mutation",
62045
62737
  auth: "admin"
62738
+ }), method(object({
62739
+ requests: array(ScanRequestSchema),
62740
+ subnets: array(string()).optional()
62741
+ }), ScanNetworkResultSchema, {
62742
+ kind: "mutation",
62743
+ auth: "admin",
62744
+ timeoutMs: 9e4
62745
+ }), method(_void(), object({ subnets: array(string()) }), { auth: "admin" }), method(object({ subnets: array(string()) }), object({ subnets: array(string()) }), {
62746
+ kind: "mutation",
62747
+ auth: "admin"
62046
62748
  }), method(_void(), TlsStatusSchema, {
62047
62749
  kind: "mutation",
62048
62750
  auth: "admin"
@@ -68884,6 +69586,13 @@ Object.freeze({
68884
69586
  access: "create",
68885
69587
  role: "admin"
68886
69588
  },
69589
+ "addonSettings.getDeclaredSettingsKeys": {
69590
+ capName: "addon-settings",
69591
+ capScope: "system",
69592
+ addonId: null,
69593
+ access: "view",
69594
+ role: "admin"
69595
+ },
68887
69596
  "addonSettings.getDeviceSettings": {
68888
69597
  capName: "addon-settings",
68889
69598
  capScope: "system",
@@ -68905,6 +69614,13 @@ Object.freeze({
68905
69614
  access: "view",
68906
69615
  role: "admin"
68907
69616
  },
69617
+ "addonSettings.unsetStoredSettingsKeys": {
69618
+ capName: "addon-settings",
69619
+ capScope: "system",
69620
+ addonId: null,
69621
+ access: "create",
69622
+ role: "admin"
69623
+ },
68908
69624
  "addonSettings.updateDeviceSettings": {
68909
69625
  capName: "addon-settings",
68910
69626
  capScope: "system",
@@ -71320,6 +72036,13 @@ Object.freeze({
71320
72036
  access: "create",
71321
72037
  role: "admin"
71322
72038
  },
72039
+ "deviceProvider.classifyDiscoveryHits": {
72040
+ capName: "device-provider",
72041
+ capScope: "system",
72042
+ addonId: null,
72043
+ access: "create",
72044
+ role: "admin"
72045
+ },
71323
72046
  "deviceProvider.createDevice": {
71324
72047
  capName: "device-provider",
71325
72048
  capScope: "system",
@@ -71348,6 +72071,13 @@ Object.freeze({
71348
72071
  access: "view",
71349
72072
  role: "admin"
71350
72073
  },
72074
+ "deviceProvider.getDiscoveryModes": {
72075
+ capName: "device-provider",
72076
+ capScope: "system",
72077
+ addonId: null,
72078
+ access: "view",
72079
+ role: "admin"
72080
+ },
71351
72081
  "deviceProvider.getDiscoveryParamsSchema": {
71352
72082
  capName: "device-provider",
71353
72083
  capScope: "system",
@@ -72125,6 +72855,13 @@ Object.freeze({
72125
72855
  access: "create",
72126
72856
  role: "admin"
72127
72857
  },
72858
+ "keychain.releaseRecordRefs": {
72859
+ capName: "keychain",
72860
+ capScope: "system",
72861
+ addonId: null,
72862
+ access: "create",
72863
+ role: "admin"
72864
+ },
72128
72865
  "keychain.resolve": {
72129
72866
  capName: "keychain",
72130
72867
  capScope: "system",
@@ -72545,6 +73282,13 @@ Object.freeze({
72545
73282
  access: "view",
72546
73283
  role: "admin"
72547
73284
  },
73285
+ "localNetwork.getScanSubnets": {
73286
+ capName: "local-network",
73287
+ capScope: "system",
73288
+ addonId: null,
73289
+ access: "view",
73290
+ role: "admin"
73291
+ },
72548
73292
  "localNetwork.getTlsStatus": {
72549
73293
  capName: "local-network",
72550
73294
  capScope: "system",
@@ -72594,6 +73338,13 @@ Object.freeze({
72594
73338
  access: "create",
72595
73339
  role: "admin"
72596
73340
  },
73341
+ "localNetwork.scanNetwork": {
73342
+ capName: "local-network",
73343
+ capScope: "system",
73344
+ addonId: null,
73345
+ access: "create",
73346
+ role: "admin"
73347
+ },
72597
73348
  "localNetwork.setAddressRole": {
72598
73349
  capName: "local-network",
72599
73350
  capScope: "system",
@@ -72615,6 +73366,13 @@ Object.freeze({
72615
73366
  access: "create",
72616
73367
  role: "admin"
72617
73368
  },
73369
+ "localNetwork.setScanSubnets": {
73370
+ capName: "local-network",
73371
+ capScope: "system",
73372
+ addonId: null,
73373
+ access: "create",
73374
+ role: "admin"
73375
+ },
72618
73376
  "localNetwork.setViewerEndpoints": {
72619
73377
  capName: "local-network",
72620
73378
  capScope: "system",
@@ -76836,6 +77594,13 @@ Object.freeze({
76836
77594
  access: "create",
76837
77595
  role: "admin"
76838
77596
  },
77597
+ "storageAdmin.dropOrphanedKind": {
77598
+ capName: "storage-admin",
77599
+ capScope: "system",
77600
+ addonId: null,
77601
+ access: "delete",
77602
+ role: "admin"
77603
+ },
76839
77604
  "storageAdmin.listKinds": {
76840
77605
  capName: "storage-admin",
76841
77606
  capScope: "system",
@@ -76857,6 +77622,27 @@ Object.freeze({
76857
77622
  access: "view",
76858
77623
  role: "admin"
76859
77624
  },
77625
+ "storageAdmin.scanStaleData": {
77626
+ capName: "storage-admin",
77627
+ capScope: "system",
77628
+ addonId: null,
77629
+ access: "create",
77630
+ role: "admin"
77631
+ },
77632
+ "storageAdmin.staleDataReport": {
77633
+ capName: "storage-admin",
77634
+ capScope: "system",
77635
+ addonId: null,
77636
+ access: "view",
77637
+ role: "admin"
77638
+ },
77639
+ "storageAdmin.unsetStaleSettingsKey": {
77640
+ capName: "storage-admin",
77641
+ capScope: "system",
77642
+ addonId: null,
77643
+ access: "create",
77644
+ role: "admin"
77645
+ },
76860
77646
  "storageEvictable.evict": {
76861
77647
  capName: "storage-evictable",
76862
77648
  capScope: "system",