@camstack/addon-post-analysis 1.2.667 → 1.2.669

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.
Files changed (16) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/{clip-model-registry-DpzRS8q3.mjs → clip-model-registry-BPvijtCz.mjs} +353 -74
  3. package/dist/{clip-model-registry-D7cqhFK3.js → clip-model-registry-CbzBwacH.js} +353 -74
  4. package/dist/embedding-encoder/index.js +6 -2
  5. package/dist/embedding-encoder/index.mjs +6 -2
  6. package/dist/pipeline-analytics/_stub.js +2 -2
  7. package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-DmTIEp0m.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-C4QK9e1e.mjs} +2 -2
  8. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-9HsFj_VL.mjs +26 -0
  9. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-anSYIl3S.mjs +26 -0
  10. package/dist/pipeline-analytics/{hostInit-ODebWXu_.mjs → hostInit-CPLvPO3K.mjs} +9 -9
  11. package/dist/pipeline-analytics/index.js +12 -3
  12. package/dist/pipeline-analytics/index.mjs +12 -3
  13. package/dist/pipeline-analytics/remoteEntry.js +1 -1
  14. package/package.json +2 -2
  15. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DAAkZRnK.mjs +0 -26
  16. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-znrgUrN1.mjs +0 -26
@@ -6069,7 +6069,7 @@ var ZodIssueCode = {
6069
6069
  var ZodFirstPartyTypeKind;
6070
6070
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
6071
6071
  //#endregion
6072
- //#region ../types/dist/sleep-hK-Dss3z.mjs
6072
+ //#region ../types/dist/sleep-DEZaLbiu.mjs
6073
6073
  /**
6074
6074
  * The audio chunk plane's byte format, and the ONE expansion from a coded
6075
6075
  * window to float samples (D455).
@@ -6662,7 +6662,7 @@ var NOT_RUN = {
6662
6662
  * throws: an unreachable or refusing engine is logged and reported `declared: false`,
6663
6663
  * because the hub (and this addon's boot) must not hang on a migration.
6664
6664
  */
6665
- async function runAddonMigrations(host, migrations, logger) {
6665
+ async function runAddonMigrations(host, migrations, logger, options = {}) {
6666
6666
  const first = migrations[0];
6667
6667
  if (first === void 0) return NOT_RUN;
6668
6668
  const owner = first.meta.owner;
@@ -6691,7 +6691,9 @@ async function runAddonMigrations(host, migrations, logger) {
6691
6691
  else if (status === "blocked") blocked.push(id);
6692
6692
  else skipped.push(id);
6693
6693
  };
6694
+ const selected = options.only === void 0 ? null : new Set(options.only);
6694
6695
  for (const step of declaration.steps) {
6696
+ if (selected !== null && !selected.has(step.id)) continue;
6695
6697
  const bound = byId.get(step.id);
6696
6698
  if (bound === void 0 || step.action === "skip") {
6697
6699
  skipped.push(step.id);
@@ -6869,6 +6871,12 @@ function projectStore(store, perNodeKeys, nodeId) {
6869
6871
  }
6870
6872
  return out;
6871
6873
  }
6874
+ /** Every scope, in the order the guard and the docs list them. */
6875
+ var SCOPED_SETTINGS_SCOPES = [
6876
+ "cluster",
6877
+ "device-defaults",
6878
+ "node"
6879
+ ];
6872
6880
  var GLOBAL_SCOPE = { kind: "global" };
6873
6881
  /** The scope every addon-wide (not per-node, not per-device) setting lives in. */
6874
6882
  function globalSecretScope() {
@@ -7718,6 +7726,13 @@ var BaseAddon = class {
7718
7726
  */
7719
7727
  secretStatePaths = [];
7720
7728
  _addonSecretTree = null;
7729
+ /**
7730
+ * D986 M4: the scoped schemas this addon declared, resolved once per capability
7731
+ * (`loadScopedSchemas`) so the sync readers (`perNodeKeys`, the secret tree, the restart check)
7732
+ * see them. Empty until loaded: the readers then fall back to {@link globalSettingsSchema}.
7733
+ */
7734
+ _scopedByCap = /* @__PURE__ */ new Map();
7735
+ _nodeKeysByCap = /* @__PURE__ */ new Map();
7721
7736
  _deviceSecretTree = null;
7722
7737
  /** Refs the last resolved config pointed at — a change to one re-resolves. */
7723
7738
  _configSecretRefs = /* @__PURE__ */ new Set();
@@ -7787,6 +7802,7 @@ var BaseAddon = class {
7787
7802
  this._ctx = context;
7788
7803
  this._secretSettings = null;
7789
7804
  }
7805
+ await this.loadScopedSchemas();
7790
7806
  await this.resolveConfig();
7791
7807
  if (secrets !== void 0) this._secretChangeUnsub = secrets.onChange((ref) => {
7792
7808
  if (this._configSecretRefs.has(ref)) this.scheduleSecretReresolve(SECRET_CHANGE_DEBOUNCE_MS);
@@ -7796,6 +7812,7 @@ var BaseAddon = class {
7796
7812
  const normalized = normalizeAddonInitResult(result);
7797
7813
  const providers = normalized && "providers" in normalized && normalized.providers ? normalized.providers : [];
7798
7814
  this._registeredCapNames = providers.map((p) => p.capability.name);
7815
+ await this.loadScopedSchemas();
7799
7816
  return normalized;
7800
7817
  }
7801
7818
  /**
@@ -7809,14 +7826,18 @@ var BaseAddon = class {
7809
7826
  * nothing; the boot continues. A migration that FAILED (or was blocked by one)
7810
7827
  * and declares `onFailure: 'block-owner'` makes this throw, so the addon does not
7811
7828
  * serve data whose shape it cannot trust; `tolerate` migrations never do.
7829
+ *
7830
+ * ALWAYS pass the whole catalogue; a phase that must run only some of it passes
7831
+ * `{ only: [ids] }` — never a filtered catalogue (the engine would judge the
7832
+ * omitted applied ids "newer").
7812
7833
  */
7813
- async runMigrations(migrations) {
7834
+ async runMigrations(migrations, options) {
7814
7835
  const api = this.ctx.api.migrations;
7815
7836
  const report = await runAddonMigrations({
7816
7837
  declareOwner: (input) => api.declareOwner.mutate(input),
7817
7838
  startMigration: (input) => api.startMigration.mutate(input),
7818
7839
  finishMigration: (input) => api.finishMigration.mutate(input)
7819
- }, migrations, this.ctx.logger);
7840
+ }, migrations, this.ctx.logger, options);
7820
7841
  if (report.blockOwner.length > 0) throw new AddonMigrationsBlockedError(report.blockOwner);
7821
7842
  return report;
7822
7843
  }
@@ -7880,6 +7901,16 @@ var BaseAddon = class {
7880
7901
  schema(schema) {
7881
7902
  return schema;
7882
7903
  }
7904
+ /**
7905
+ * The capability names whose scoped settings schemas ({@link clusterSettingsSchema},
7906
+ * {@link nodeSettingsSchema}, {@link deviceDefaultsSchema}) this addon declares. Read BEFORE
7907
+ * `onInitialize`, when the providers it registers are not known yet: the first config resolve
7908
+ * must already know which keys are per node. Names the providers register at init are added
7909
+ * once known.
7910
+ */
7911
+ scopedSettingsCapabilities() {
7912
+ return [];
7913
+ }
7883
7914
  /** Override to provide global-level settings UI schema. */
7884
7915
  globalSettingsSchema(_cap) {
7885
7916
  return null;
@@ -7923,7 +7954,8 @@ var BaseAddon = class {
7923
7954
  return [];
7924
7955
  }
7925
7956
  async getGlobalSettings(overlay, cap, nodeId) {
7926
- const schema = this.globalSettingsSchema(cap);
7957
+ await this.loadScopedSchemas(cap);
7958
+ const schema = this.globalSettingsSchema(cap) ?? this.scopedFormSchema(cap);
7927
7959
  if (!schema) return { sections: [] };
7928
7960
  const projected = await this.readRawProjectedStore(nodeId, cap);
7929
7961
  return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(projected, overlay) : projected, { inlineSecretsAsViews: true });
@@ -7958,10 +7990,9 @@ var BaseAddon = class {
7958
7990
  *
7959
7991
  * Override it only when an `instanceMode: multiple` addon's instances differ: an NVR integration
7960
7992
  * needs its connection fields, the default integration the addon's own sections. It is the
7961
- * SHAPE of the form only — the values live in `integrations.getSettings`, and the write path is
7962
- * unchanged (`integrations.setSettings` for an account integration).
7993
+ * SHAPE of the form only — the values live on the instance record.
7963
7994
  */
7964
- async integrationSettingsSchema(_instance) {
7995
+ async integrationFormSchema(_instance) {
7965
7996
  return null;
7966
7997
  }
7967
7998
  /**
@@ -8007,7 +8038,7 @@ var BaseAddon = class {
8007
8038
  * per-instance schema when it declares one, else its global schema (what they always showed).
8008
8039
  */
8009
8040
  async getIntegrationFormSchema(instance, nodeId) {
8010
- const scoped = await this.integrationSettingsSchema(instance);
8041
+ const scoped = await this.integrationFormSchema(instance);
8011
8042
  if (scoped === null) return this.getGlobalSettings(void 0, void 0, nodeId);
8012
8043
  return hydrateSchema(scoped, {}, { inlineSecretsAsViews: true });
8013
8044
  }
@@ -8055,6 +8086,7 @@ var BaseAddon = class {
8055
8086
  * resolved blob back is a no-op for every unchanged secret.
8056
8087
  */
8057
8088
  async resolveGlobalStore(nodeId, cap) {
8089
+ await this.loadScopedSchemas(cap);
8058
8090
  const raw = await this._ctx?.settings?.readAddonStore() ?? {};
8059
8091
  return this.projectForNode(raw, nodeId, cap);
8060
8092
  }
@@ -8065,6 +8097,7 @@ var BaseAddon = class {
8065
8097
  }
8066
8098
  /** The PROJECTED store with its markers — the form hydrate only (D798). */
8067
8099
  async readRawProjectedStore(nodeId, cap) {
8100
+ await this.loadScopedSchemas(cap);
8068
8101
  const raw = await this.rawSettings()?.readAddonStore() ?? {};
8069
8102
  return this.projectForNode(raw, nodeId, cap);
8070
8103
  }
@@ -8167,6 +8200,7 @@ var BaseAddon = class {
8167
8200
  await this.applyGlobalPatch(patch, nodeId);
8168
8201
  }
8169
8202
  async applyGlobalPatch(barePatch, nodeId) {
8203
+ await this.loadScopedSchemas();
8170
8204
  const keys = this.perNodeKeys();
8171
8205
  const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
8172
8206
  const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
@@ -8180,7 +8214,8 @@ var BaseAddon = class {
8180
8214
  await this.resolveConfig();
8181
8215
  await this.onConfigChanged();
8182
8216
  this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
8183
- this.maybeAutoRestart(Object.keys(barePatch), this.globalSettingsSchema());
8217
+ const legacy = this.globalSettingsSchema();
8218
+ this.maybeAutoRestart(Object.keys(barePatch), [...legacy === null ? [] : [legacy], ...this.loadedScopedSchemas().map((entry) => entry.schema)]);
8184
8219
  }
8185
8220
  /**
8186
8221
  * The set of field keys the global settings schema declares `perNode: true`
@@ -8188,14 +8223,67 @@ var BaseAddon = class {
8188
8223
  * declarations). Empty set ⇒ every per-node code path is bypassed and the
8189
8224
  * settings API behaves exactly like the legacy node-agnostic one.
8190
8225
  */
8191
- _perNodeKeysCache = /* @__PURE__ */ new Map();
8226
+ /**
8227
+ * Load the scoped schemas of every capability this addon declares (the override and the
8228
+ * providers registered at init, plus `extraCap`) that is not loaded yet, then drop what was
8229
+ * derived from the previous set. Idempotent.
8230
+ */
8231
+ async loadScopedSchemas(extraCap) {
8232
+ const caps = new Set([
8233
+ ...this.scopedSettingsCapabilities(),
8234
+ ...this._registeredCapNames,
8235
+ ...extraCap === void 0 ? [] : [extraCap]
8236
+ ]);
8237
+ let added = false;
8238
+ for (const cap of caps) {
8239
+ if (this._scopedByCap.has(cap)) continue;
8240
+ const loaded = [];
8241
+ for (const scope of SCOPED_SETTINGS_SCOPES) {
8242
+ const schema = await this.getScopedSettingsSchema(scope, cap);
8243
+ if (schema !== null) loaded.push({
8244
+ scope,
8245
+ schema
8246
+ });
8247
+ }
8248
+ this._scopedByCap.set(cap, loaded);
8249
+ added = true;
8250
+ }
8251
+ if (!added) return;
8252
+ this._nodeKeysByCap = /* @__PURE__ */ new Map();
8253
+ this._addonSecretTree = null;
8254
+ }
8255
+ /**
8256
+ * The form the settings API serves while an addon declares no {@link globalSettingsSchema}: the
8257
+ * sections of its scoped schemas, so dropping the old hook does not blank the form.
8258
+ */
8259
+ scopedFormSchema(cap) {
8260
+ const byId = /* @__PURE__ */ new Map();
8261
+ for (const entry of this.loadedScopedSchemas(cap)) for (const section of entry.schema.sections) if (!byId.has(section.id)) byId.set(section.id, section);
8262
+ return byId.size === 0 ? null : { sections: [...byId.values()] };
8263
+ }
8264
+ /** Every loaded scoped schema of `cap`, or of every loaded capability. */
8265
+ loadedScopedSchemas(cap) {
8266
+ if (cap !== void 0) return this._scopedByCap.get(cap) ?? [];
8267
+ return [...this._scopedByCap.values()].flat();
8268
+ }
8269
+ /**
8270
+ * The field keys that are stored per node: every field of a {@link nodeSettingsSchema}, plus
8271
+ * the fields the legacy {@link globalSettingsSchema} still marks `perNode: true`. Derived once
8272
+ * per `cap` argument and memoized (schemas are static declarations). Empty set ⇒ every
8273
+ * per-node code path is bypassed and the settings API behaves like the node-agnostic one.
8274
+ * Call {@link loadScopedSchemas} first (the async seams do).
8275
+ */
8192
8276
  perNodeKeys(cap) {
8193
8277
  const cacheKey = cap ?? "";
8194
- const cached = this._perNodeKeysCache.get(cacheKey);
8278
+ const cached = this._nodeKeysByCap.get(cacheKey);
8195
8279
  if (cached) return cached;
8196
- const schema = this.globalSettingsSchema(cap);
8197
- const keys = new Set(schema ? schema.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
8198
- this._perNodeKeysCache.set(cacheKey, keys);
8280
+ const legacy = this.globalSettingsSchema(cap);
8281
+ const keys = new Set(legacy ? legacy.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
8282
+ for (const { scope, schema } of this.loadedScopedSchemas(cap)) {
8283
+ if (scope !== "node") continue;
8284
+ for (const section of schema.sections) for (const key of collectValueFieldKeys(section.fields)) keys.add(key);
8285
+ }
8286
+ this._nodeKeysByCap.set(cacheKey, keys);
8199
8287
  return keys;
8200
8288
  }
8201
8289
  /**
@@ -8205,10 +8293,9 @@ var BaseAddon = class {
8205
8293
  * time to return its response before the addon is torn down and
8206
8294
  * re-initialised by `AddonRegistryService.restartAddon`.
8207
8295
  */
8208
- maybeAutoRestart(patchKeys, schema) {
8209
- if (!schema) return;
8296
+ maybeAutoRestart(patchKeys, schemas) {
8210
8297
  const restartKeys = /* @__PURE__ */ new Set();
8211
- for (const section of schema.sections) for (const field of section.fields) {
8298
+ for (const schema of schemas) for (const section of schema.sections) for (const field of section.fields) {
8212
8299
  if (field.type === "separator" || field.type === "info") continue;
8213
8300
  if (field.requiresRestart) restartKeys.add(field.key);
8214
8301
  }
@@ -8404,12 +8491,14 @@ var BaseAddon = class {
8404
8491
  * here, in the addon's own process, so the hub keeps no copy of it.
8405
8492
  */
8406
8493
  async getDeclaredSettingsKeys() {
8494
+ await this.loadScopedSchemas();
8407
8495
  return { keys: [...this.declaredKeySet()].toSorted() };
8408
8496
  }
8409
8497
  declaredKeySet() {
8410
8498
  const keys = new Set(Object.keys(this.defaults));
8411
8499
  const schema = this.globalSettingsSchema();
8412
8500
  if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
8501
+ for (const { schema: scoped } of this.loadedScopedSchemas()) for (const section of scoped.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
8413
8502
  for (const key of this._declaredStateKeys) keys.add(key);
8414
8503
  for (const path of this.secretStatePaths) {
8415
8504
  const root = path.split(/[.[]/, 1)[0];
@@ -8505,7 +8594,11 @@ var BaseAddon = class {
8505
8594
  }
8506
8595
  /** The addon store's secret tree: schema fields by TYPE, plus `secretStatePaths`. */
8507
8596
  addonSecretTree() {
8508
- this._addonSecretTree ??= mergeSecretStatePaths(secretTreeFromSchema(this.globalSettingsSchema()), this.secretStatePaths);
8597
+ if (this._addonSecretTree === null) {
8598
+ const tree = new Map(secretTreeFromSchema(this.globalSettingsSchema()));
8599
+ for (const { schema } of this.loadedScopedSchemas()) for (const [key, node] of secretTreeFromSchema(schema)) tree.set(key, node);
8600
+ this._addonSecretTree = mergeSecretStatePaths(tree, this.secretStatePaths);
8601
+ }
8509
8602
  return this._addonSecretTree;
8510
8603
  }
8511
8604
  deviceSecretTree() {
@@ -8755,6 +8848,23 @@ function dropPerNodeFields(fields) {
8755
8848
  }
8756
8849
  return kept;
8757
8850
  }
8851
+ /** The keys of the value-holding fields, recursing into layout containers. */
8852
+ function collectValueFieldKeys(fields) {
8853
+ const collected = [];
8854
+ for (const field of fields) {
8855
+ if (field.type === "group") {
8856
+ collected.push(...collectValueFieldKeys(field.fields));
8857
+ continue;
8858
+ }
8859
+ if (field.type === "sub-tabs") {
8860
+ for (const tab of field.tabs) collected.push(...collectValueFieldKeys(tab.fields));
8861
+ continue;
8862
+ }
8863
+ if (field.type === "separator" || field.type === "info") continue;
8864
+ if ("key" in field && typeof field.key === "string") collected.push(field.key);
8865
+ }
8866
+ return collected;
8867
+ }
8758
8868
  function collectFieldKeys(fields) {
8759
8869
  const collected = [];
8760
8870
  for (const field of fields) {
@@ -8847,6 +8957,10 @@ var RECORD_SETTINGS_MAX_ROWS = 5e4;
8847
8957
  * (`collectZodSecretPaths`): dotted keys, `[]` for "each element of this array"
8848
8958
  * (`auth.accessToken`, `headers[].value`).
8849
8959
  *
8960
+ * `{}` after a key means "each value of this map" (`applianceSecrets{}`): the map's own key
8961
+ * names the value (`applianceSecrets{fridge}`), so no id key is declared and a body that drops a
8962
+ * key releases exactly that key's secret.
8963
+ *
8850
8964
  * Array elements are matched by a DECLARED id key (`secretArrayKeys`:
8851
8965
  * `{ headers: 'name' }`), never by index, so reordering a list keeps every
8852
8966
  * keychain ref (the same rule as `arrayItemFieldPath`). The concrete path of a
@@ -8859,11 +8973,13 @@ function parseSecretPath(path) {
8859
8973
  if (path.length === 0) throw new InvalidRecordError("an empty secret path");
8860
8974
  return path.split(".").map((raw) => {
8861
8975
  const each = raw.endsWith("[]");
8862
- const key = each ? raw.slice(0, -2) : raw;
8976
+ const map = raw.endsWith("{}");
8977
+ const key = each || map ? raw.slice(0, -2) : raw;
8863
8978
  if (key.length === 0) throw new InvalidRecordError(`secret path "${path}" has an empty segment`);
8864
8979
  return {
8865
8980
  key,
8866
- each
8981
+ each,
8982
+ map
8867
8983
  };
8868
8984
  });
8869
8985
  }
@@ -8950,7 +9066,8 @@ function isZodSecret(schema) {
8950
9066
  }
8951
9067
  /**
8952
9068
  * Every declared secret path in a config schema: dotted keys, `[]` for "each
8953
- * element of this array" (`connection.password`, `rows[].token`). A declared
9069
+ * element of this array" (`connection.password`, `rows[].token`), `{}` for "each value of this
9070
+ * map" (`applianceSecrets{}`). A declared
8954
9071
  * object or array is reported as one path and not descended into — everything
8955
9072
  * under it is secret.
8956
9073
  */
@@ -8961,6 +9078,7 @@ function collectZodSecretPaths(schema, prefix = "") {
8961
9078
  const def = defOf(layer);
8962
9079
  if (def.type === "object" && def.shape) for (const [key, child] of Object.entries(def.shape)) paths.push(...collectZodSecretPaths(child, prefix ? `${prefix}.${key}` : key));
8963
9080
  else if (def.type === "array" && def.element) paths.push(...collectZodSecretPaths(def.element, `${prefix}[]`));
9081
+ else if (def.type === "record" && def.valueType) paths.push(...collectZodSecretPaths(def.valueType, `${prefix}{}`));
8964
9082
  else if (def.type === "union" && def.options) for (const option of def.options) paths.push(...collectZodSecretPaths(option, prefix));
8965
9083
  else if (def.type === "intersection") {
8966
9084
  if (def.left) paths.push(...collectZodSecretPaths(def.left, prefix));
@@ -9100,7 +9218,7 @@ function defineRecordKinds(specs) {
9100
9218
  retention: spec.retention,
9101
9219
  deviceRemoval: spec.deviceRemoval,
9102
9220
  maxDataBytes: spec.maxDataBytes,
9103
- secretPaths: [...secretPaths],
9221
+ secretPaths: [...new Set([...secretPaths, ...spec.retiredSecretPaths ?? []])],
9104
9222
  secretArrayKeys: spec.secretArrayKeys === void 0 ? void 0 : { ...spec.secretArrayKeys },
9105
9223
  transactFamily: spec.transactFamily,
9106
9224
  entry: spec.entry === void 0 ? void 0 : {
@@ -12009,7 +12127,7 @@ function sleep(ms) {
12009
12127
  return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
12010
12128
  }
12011
12129
  //#endregion
12012
- //#region ../types/dist/composition-BSGHhcr8.mjs
12130
+ //#region ../types/dist/composition-pOHbTzRB.mjs
12013
12131
  /**
12014
12132
  * Deterministic SHA-256 hash of an arbitrary serialisable value. The
12015
12133
  * canonical form sorts object keys alphabetically at every depth so two
@@ -14678,7 +14796,13 @@ var MigrationLedgerEntrySchema = object({
14678
14796
  * joined by the engine from the catalogues it knows this boot. Absent when no
14679
14797
  * catalogue lists the id (a newer database).
14680
14798
  */
14681
- meta: MigrationMetaSchema.optional()
14799
+ meta: MigrationMetaSchema.optional(),
14800
+ /**
14801
+ * Set when this is an applied row of an addon owner whose catalogue the answering
14802
+ * process has not loaded (the owner has not declared yet): the row is applied, the
14803
+ * process just cannot judge it. Never `skipped-newer` — that needs the owner loaded.
14804
+ */
14805
+ ownerNotLoaded: boolean().optional()
14682
14806
  });
14683
14807
  /** A catalogued migration that is not applied, with where it stands. */
14684
14808
  var MigrationPendingEntrySchema = object({
@@ -51210,6 +51334,9 @@ var RunningProposalEvidenceSchema = object({
51210
51334
  runsFound: number().int().nonnegative(),
51211
51335
  pausesFound: number().int().nonnegative(),
51212
51336
  longestPauseMs: number().nonnegative(),
51337
+ /** Episodes shorter than ~90 s (an anti-crease pulse, a display waking): never a run, never a pause. */
51338
+ pulsesFound: number().int().nonnegative(),
51339
+ longestPulseMs: number().nonnegative(),
51213
51340
  idleFloorW: number(),
51214
51341
  noiseCeilingW: number(),
51215
51342
  plateauW: number(),
@@ -52500,7 +52627,27 @@ method(object({
52500
52627
  * exposes only the COMMON surface: link state, supported device
52501
52628
  * kinds (so the UI can filter the device picker), and per-device
52502
52629
  * expose/unexpose.
52503
- */
52630
+ *
52631
+ * INSTANCES. One export addon can serve several independent targets (a
52632
+ * HomeKit bridge, a Home Assistant broker). `listInstances` names them.
52633
+ * `instanceId` on a READ is optional: absent = the whole addon (linked if any
52634
+ * enabled instance is, `exposedDeviceCount` = distinct devices, `setup` = the
52635
+ * default instance's); present = that instance; an id the provider does not
52636
+ * serve is refused with `ExportInstanceUnknownError` (`export-instance-unknown`),
52637
+ * never answered empty. `instanceId` on a WRITE is REQUIRED: a membership
52638
+ * change always names the target it changes. A single-instance provider
52639
+ * accepts an absent id on a read or its own id, and refuses any other.
52640
+ */
52641
+ /** Which instance of an export addon a call addresses. Absent on a read = the whole addon. */
52642
+ var InstanceIdSchema$1 = string().min(1);
52643
+ var InstancePinSchema = object({ instanceId: InstanceIdSchema$1.optional() });
52644
+ /** One independent export target of an addon. */
52645
+ var ExportInstanceSchema = object({
52646
+ instanceId: InstanceIdSchema$1,
52647
+ name: string(),
52648
+ enabled: boolean(),
52649
+ kind: string().optional()
52650
+ });
52504
52651
  var LinkStateSchema = _enum([
52505
52652
  "unlinked",
52506
52653
  "linked",
@@ -52551,10 +52698,14 @@ var ExposedDeviceSchema = object({
52551
52698
  capabilities: array(string()).optional()
52552
52699
  });
52553
52700
  var ExposeInputSchema = object({
52701
+ instanceId: InstanceIdSchema$1,
52554
52702
  deviceId: string(),
52555
52703
  capabilities: array(string()).optional()
52556
52704
  });
52557
- var UnexposeInputSchema = object({ deviceId: string() });
52705
+ var UnexposeInputSchema = object({
52706
+ instanceId: InstanceIdSchema$1,
52707
+ deviceId: string()
52708
+ });
52558
52709
  /**
52559
52710
  * What an exporter may expose besides devices (D968). An action and a
52560
52711
  * notification target are LIST ENTRIES, never devices (D954): they enter an
@@ -52583,15 +52734,21 @@ var ExposedEntitySchema = object({
52583
52734
  entity: ExportEntityRefSchema,
52584
52735
  exposedAs: string().optional()
52585
52736
  });
52586
- var ExposeEntityInputSchema = object({ entity: ExportEntityRefSchema });
52587
- var UnexposeEntityInputSchema = object({ entity: ExportEntityRefSchema });
52588
- method(_void(), DeviceExportStatusSchema), method(_void(), array(DeviceKindSchema)), method(_void(), array(ExposedDeviceSchema)), method(ExposeInputSchema, _void(), {
52737
+ var ExposeEntityInputSchema = object({
52738
+ instanceId: InstanceIdSchema$1,
52739
+ entity: ExportEntityRefSchema
52740
+ });
52741
+ var UnexposeEntityInputSchema = object({
52742
+ instanceId: InstanceIdSchema$1,
52743
+ entity: ExportEntityRefSchema
52744
+ });
52745
+ method(InstancePinSchema, DeviceExportStatusSchema), method(_void(), array(ExportInstanceSchema)), method(InstancePinSchema, array(DeviceKindSchema)), method(InstancePinSchema, array(ExposedDeviceSchema)), method(ExposeInputSchema, _void(), {
52589
52746
  kind: "mutation",
52590
52747
  auth: "admin"
52591
52748
  }), method(UnexposeInputSchema, _void(), {
52592
52749
  kind: "mutation",
52593
52750
  auth: "admin"
52594
- }), optionalMethod(_void(), array(ExportEntityKindSchema)), optionalMethod(_void(), array(ExposedEntitySchema)), optionalMethod(ExposeEntityInputSchema, _void(), {
52751
+ }), optionalMethod(_void(), array(ExportEntityKindSchema)), optionalMethod(InstancePinSchema, array(ExposedEntitySchema)), optionalMethod(ExposeEntityInputSchema, _void(), {
52595
52752
  kind: "mutation",
52596
52753
  auth: "admin"
52597
52754
  }), optionalMethod(UnexposeEntityInputSchema, _void(), {
@@ -57092,11 +57249,7 @@ var CreateIntegrationInputSchema = object({
57092
57249
  info: record(string(), unknown()).optional(),
57093
57250
  /** `stableId` of the discovery candidate the form was seeded from (D984). */
57094
57251
  originStableId: string().optional(),
57095
- settings: record(string(), unknown()).optional(),
57096
- /** Keys of `settings` the form's schema declares secret (by type) — kept in
57097
- * the keychain, not the table (D787). */
57098
- secretKeys: array(string()).optional(),
57099
- skipRestart: boolean().optional()
57252
+ settings: record(string(), unknown()).optional()
57100
57253
  });
57101
57254
  var UpdateIntegrationInputSchema = object({
57102
57255
  id: string(),
@@ -57117,16 +57270,6 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
57117
57270
  }), method(object({ id: string() }), DeleteIntegrationResultSchema, {
57118
57271
  kind: "mutation",
57119
57272
  auth: "admin"
57120
- }), method(object({ id: string() }), record(string(), unknown()), { auth: "admin" }), method(object({
57121
- id: string(),
57122
- settings: record(string(), unknown()),
57123
- /** Keys the form's schema declares secret (by type) — keychain-stored (D787). */
57124
- secretKeys: array(string()).optional(),
57125
- /** The integration's own addon applies the change in-process and asks the hub not to respawn it. */
57126
- skipRestart: boolean().optional()
57127
- }), object({ success: boolean() }), {
57128
- kind: "mutation",
57129
- auth: "admin"
57130
57273
  }), method(_void(), array(AvailableIntegrationTypeSchema), { auth: "admin" }), method(object({
57131
57274
  addonId: string(),
57132
57275
  settings: record(string(), unknown())
@@ -57155,13 +57298,65 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
57155
57298
  */
57156
57299
  var ConfigSchemaPassthrough$3 = custom();
57157
57300
  var IntegrationInstanceIdSchema = string().min(1);
57301
+ var IntegrationInstanceModeSchema = _enum(["single", "multiple"]);
57302
+ /**
57303
+ * One kind of instance a provider can create (an adapter, an account type). The sign-in and
57304
+ * connect fields follow the `broker` contract (`broker.cap.ts`): `signIn.basePath` is a
57305
+ * hub-relative route set speaking the sign-in contract, `connectPath` / `disconnectPath` open an
57306
+ * interactive account link (popup) and may carry `{{brokerId}}`, which the UI replaces with the
57307
+ * instance id.
57308
+ */
57309
+ var IntegrationInstanceKindSchema = object({
57310
+ /** Stable id of the kind, sent back as `createInstance.kind`. */
57311
+ id: string().min(1),
57312
+ label: string().min(1),
57313
+ /** The kind signs in through a server-held interactive session. */
57314
+ signIn: object({
57315
+ basePath: string().min(1),
57316
+ methods: array(_enum(["device-code", "proxy"])).optional()
57317
+ }).optional(),
57318
+ /** Relative hub path to start an interactive account link of an instance of this kind. */
57319
+ connectPath: string().min(1).optional(),
57320
+ /** Relative hub path to disconnect that account. */
57321
+ disconnectPath: string().min(1).optional()
57322
+ });
57158
57323
  var IntegrationInstanceDescriptionSchema = object({
57324
+ /** What the instances are for the Integrations page; see {@link IntegrationInstanceRoleSchema}. */
57325
+ role: _enum([
57326
+ "devices",
57327
+ "services",
57328
+ "both"
57329
+ ]),
57330
+ /** How the devices of an instance are found; absent means `{ mode: 'none' }`. */
57331
+ discovery: object({
57332
+ mode: _enum([
57333
+ "none",
57334
+ "lan",
57335
+ "account"
57336
+ ]),
57337
+ scopedToInstance: boolean()
57338
+ }).optional(),
57159
57339
  /** `single`: at most one instance can exist (Loki). `multiple`: an account each. */
57160
- instanceMode: _enum(["single", "multiple"]),
57340
+ instanceMode: IntegrationInstanceModeSchema,
57161
57341
  /** What the instance is, for the UI (`destination`, `relay`, `account`). */
57162
57342
  kind: string(),
57163
57343
  /** True when `testInstance` really probes the remote. */
57164
- canTest: boolean()
57344
+ canTest: boolean(),
57345
+ /**
57346
+ * The kinds an instance can be of. Absent or one entry: nothing to choose; more than one: the
57347
+ * Add flow asks which first and sends it as `createInstance.kind`.
57348
+ */
57349
+ kinds: array(IntegrationInstanceKindSchema).optional(),
57350
+ /**
57351
+ * The provider accepts `createInstance.id` for an id that once existed (an adopted id, because
57352
+ * devices carry it). The UI then offers "restore under its old id" on Add. Absent means no.
57353
+ */
57354
+ allowRestoreId: boolean().optional(),
57355
+ /**
57356
+ * `createInstance` without an `id` works: the provider mints it. Absent means the caller must
57357
+ * send an id (the UI then mints an opaque one).
57358
+ */
57359
+ mintsId: boolean().optional()
57165
57360
  });
57166
57361
  var IntegrationInstanceStatusSchema = object({
57167
57362
  state: _enum([
@@ -57170,7 +57365,9 @@ var IntegrationInstanceStatusSchema = object({
57170
57365
  "error",
57171
57366
  "unknown"
57172
57367
  ]),
57173
- detail: string().optional()
57368
+ detail: string().optional(),
57369
+ /** A short human word for the state ("Signed in", "Token expired"); shown instead of `state`. */
57370
+ label: string().optional()
57174
57371
  });
57175
57372
  var IntegrationInstanceSchema = object({
57176
57373
  id: IntegrationInstanceIdSchema,
@@ -57178,13 +57375,18 @@ var IntegrationInstanceSchema = object({
57178
57375
  /** The addon's own rule (an URL is present, a client id is present...). */
57179
57376
  configured: boolean(),
57180
57377
  /**
57181
- * The instance's own on/off switch (D996): always present, derived from the record body
57182
- * (`enabled !== false`, so a row written before the field existed is on).
57378
+ * The instance's own on/off switch (D996), derived from the record body (`enabled !== false`,
57379
+ * so a row written before the field existed is on). Absent when the `integrations` registry
57380
+ * owns the switch (`describe().role` is `devices`): read it from the registry row.
57183
57381
  */
57184
- enabled: boolean(),
57382
+ enabled: boolean().optional(),
57383
+ /** The kind of this instance (one of `describe().kinds[].id`), when the provider has kinds. */
57384
+ kind: string().optional(),
57185
57385
  /** Values for the instance form; a secret is a `SecretFieldView`, never text. */
57186
57386
  formValues: record(string(), unknown()),
57187
- status: IntegrationInstanceStatusSchema.optional()
57387
+ status: IntegrationInstanceStatusSchema.optional(),
57388
+ /** Epoch ms of the last probe of the remote that produced `status`; absent when never checked. */
57389
+ lastCheckedAt: number().optional()
57188
57390
  });
57189
57391
  var IntegrationInstancePatchSchema = object({
57190
57392
  id: IntegrationInstanceIdSchema,
@@ -57193,8 +57395,9 @@ var IntegrationInstancePatchSchema = object({
57193
57395
  values: record(string(), unknown())
57194
57396
  });
57195
57397
  method(_void(), IntegrationInstanceDescriptionSchema, { auth: "admin" }), method(object({ id: IntegrationInstanceIdSchema.nullable() }), ConfigSchemaPassthrough$3, { auth: "admin" }), method(_void(), array(IntegrationInstanceSchema).readonly(), { auth: "admin" }), method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable(), { auth: "admin" }), method(object({
57196
- id: IntegrationInstanceIdSchema,
57197
- name: string().min(1)
57398
+ id: IntegrationInstanceIdSchema.optional(),
57399
+ name: string().min(1),
57400
+ kind: string().min(1).optional()
57198
57401
  }), object({ id: string() }), {
57199
57402
  kind: "mutation",
57200
57403
  auth: "admin"
@@ -59354,6 +59557,11 @@ var LogEntrySchema = object({
59354
59557
  * flight, and not asking for the next slice ends the scan.
59355
59558
  */
59356
59559
  var LogReadQuerySchema = object({
59560
+ /**
59561
+ * Which destination INSTANCE answers (a destination addon may hold several records). Absent ⇒
59562
+ * the provider's first readable one, so a caller written before instances existed is unchanged.
59563
+ */
59564
+ instanceId: string().min(1).max(200).optional(),
59357
59565
  /** Correlates `cancelRead`. Caller-chosen, unique per in-flight read. */
59358
59566
  requestId: string().min(1).max(64).optional(),
59359
59567
  /** The device the line is about (`tags.deviceId`, numeric). */
@@ -59416,6 +59624,8 @@ var LogReadResultSchema = object({
59416
59624
  /** True when `cancelRead` ended this slice early. */
59417
59625
  cancelled: boolean().optional()
59418
59626
  });
59627
+ /** Names one destination instance; absent ⇒ the provider's first readable one. */
59628
+ var LogInstanceSelectorSchema = object({ instanceId: string().min(1).max(200).optional() });
59419
59629
  var LogReadCapabilitiesSchema = object({
59420
59630
  /** False ⇒ this destination only ships lines (console, forwarder, an unconfigured Loki). */
59421
59631
  readable: boolean(),
@@ -59434,7 +59644,7 @@ var LogReadCapabilitiesSchema = object({
59434
59644
  /** How far back the store reaches, when the provider knows (days). */
59435
59645
  retentionDays: number().nullable()
59436
59646
  });
59437
- method(LogEntrySchema, _void(), { kind: "mutation" }), optionalMethod(_void(), LogReadCapabilitiesSchema, { auth: "admin" }), optionalMethod(LogReadQuerySchema, LogReadResultSchema, { auth: "admin" }), optionalMethod(object({ requestId: string().min(1).max(64) }), object({ cancelled: boolean() }), {
59647
+ method(LogEntrySchema, _void(), { kind: "mutation" }), optionalMethod(LogInstanceSelectorSchema, LogReadCapabilitiesSchema, { auth: "admin" }), optionalMethod(LogReadQuerySchema, LogReadResultSchema, { auth: "admin" }), optionalMethod(object({ requestId: string().min(1).max(64) }), object({ cancelled: boolean() }), {
59438
59648
  kind: "mutation",
59439
59649
  auth: "admin"
59440
59650
  });
@@ -60131,6 +60341,27 @@ method(object({}), object({ entries: array(MigrationLedgerEntrySchema) }), { aut
60131
60341
  }), {
60132
60342
  kind: "mutation",
60133
60343
  auth: "admin"
60344
+ }), method(object({ integrationId: string().min(1) }), record(string(), unknown()), { auth: "admin" }), method(object({ integrationId: string().min(1) }), object({
60345
+ rows: number().int().nonnegative(),
60346
+ secrets: number().int().nonnegative()
60347
+ }), {
60348
+ kind: "mutation",
60349
+ auth: "admin"
60350
+ }), method(object({
60351
+ integrationId: string().min(1),
60352
+ patch: record(string(), unknown())
60353
+ }), object({ written: boolean() }), {
60354
+ kind: "mutation",
60355
+ auth: "admin"
60356
+ }), method(object({
60357
+ integrationId: string().min(1),
60358
+ keys: array(string().min(1))
60359
+ }), object({
60360
+ present: boolean(),
60361
+ removed: record(string(), unknown())
60362
+ }), {
60363
+ kind: "mutation",
60364
+ auth: "admin"
60134
60365
  });
60135
60366
  method(object({
60136
60367
  sourceUrl: string(),
@@ -83276,6 +83507,25 @@ function buildEventKindDescriptor(kind, source) {
83276
83507
  }
83277
83508
  };
83278
83509
  }
83510
+ /**
83511
+ * What a remote key is CALLED and what it LOOKS like, for every role (D963),
83512
+ * shared by the admin UI and the viewer. A role is data on the wire; each
83513
+ * client resolves its name from `label` (English and Italian side by side) and
83514
+ * its glyph from `icon`, a lucide id from the shared icon catalogue — the
83515
+ * server sends an id, a client maps it to its own icon set.
83516
+ *
83517
+ * `legend` is text printed on the key INSTEAD of a glyph (a digit has no
83518
+ * pictogram): a client that can draw text shows it, one that cannot falls back
83519
+ * to `icon`.
83520
+ */
83521
+ function digit(n) {
83522
+ return {
83523
+ label: t(n, n),
83524
+ icon: "hash",
83525
+ legend: n
83526
+ };
83527
+ }
83528
+ digit("0"), digit("1"), digit("2"), digit("3"), digit("4"), digit("5"), digit("6"), digit("7"), digit("8"), digit("9");
83279
83529
  DeviceFeature.MediaPlayerPower, DeviceFeature.MediaPlayerVolumeStep, DeviceFeature.MediaPlayerChannelStep, DeviceFeature.MediaPlayerMute, DeviceFeature.MediaPlayerStop, DeviceFeature.MediaPlayerNext, DeviceFeature.MediaPlayerPrevious;
83280
83530
  var btn = (idSuffix, row, col, init) => ({
83281
83531
  idSuffix,
@@ -83414,11 +83664,15 @@ discriminatedUnion("kind", [object({
83414
83664
  * 1. idle floor F = time-weighted median; noise ceiling N = highest value seen
83415
83665
  * near F. A device that is not near one level most of the time has no floor.
83416
83666
  * 2. coarse activity = above max(2N, N + 5 W); consecutive stretches are episodes.
83417
- * 3. gaps between episodes split in two by the largest log-scale jump (>= 3x):
83418
- * the short ones are pauses INSIDE a run (a soak), the long ones separate runs.
83667
+ * 3. an episode shorter than `PULSE_MAX_MS` is a PULSE (an anti-crease tumble, a
83668
+ * display waking): it is neither a run nor a pause. Among the remaining
83669
+ * episodes, gaps over `PAUSE_MAX_MS` always separate runs; the rest split in
83670
+ * two by the largest log-scale jump (>= 3x): the short ones are pauses INSIDE
83671
+ * a run (a soak), the long ones separate runs. A stretch under `MIN_RUN_MS`
83672
+ * is no run.
83419
83673
  * 4. T = geometric mean of N and the plateau's 10th percentile, snapped to a
83420
- * grid a person would type; stop dwell covers the longest pause; start dwell
83421
- * covers the longest standby blip above T.
83674
+ * grid a person would type; stop dwell covers the longest pause and the
83675
+ * kind's floor; start dwell covers the longest pulse and standby blip above T.
83422
83676
  * 5. the numbers are proved by REPLAY through the real `dwell` evaluation: each
83423
83677
  * run must hold exactly one true interval and nothing may flap outside.
83424
83678
  */
@@ -83429,6 +83683,10 @@ var MINUTE_MS$1 = 60 * SECOND_MS;
83429
83683
  30 * SECOND_MS;
83430
83684
  60 * SECOND_MS;
83431
83685
  20 * MINUTE_MS$1;
83686
+ 90 * SECOND_MS;
83687
+ 30 * MINUTE_MS$1;
83688
+ 10 * MINUTE_MS$1;
83689
+ DeviceType.Dryer, 3 * MINUTE_MS$1, DeviceType.Dishwasher, 3 * MINUTE_MS$1, DeviceType.Washer, 10 * MINUTE_MS$1;
83432
83690
  10 * SECOND_MS;
83433
83691
  10 * SECOND_MS;
83434
83692
  DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverStoppable, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
@@ -86613,6 +86871,13 @@ Object.freeze({
86613
86871
  access: "view",
86614
86872
  role: "admin"
86615
86873
  },
86874
+ "deviceExport.listInstances": {
86875
+ capName: "device-export",
86876
+ capScope: "system",
86877
+ addonId: null,
86878
+ access: "view",
86879
+ role: "admin"
86880
+ },
86616
86881
  "deviceExport.listSupportedDeviceKinds": {
86617
86882
  capName: "device-export",
86618
86883
  capScope: "system",
@@ -88300,13 +88565,6 @@ Object.freeze({
88300
88565
  access: "view",
88301
88566
  role: "admin"
88302
88567
  },
88303
- "integrations.getSettings": {
88304
- capName: "integrations",
88305
- capScope: "system",
88306
- addonId: null,
88307
- access: "view",
88308
- role: "admin"
88309
- },
88310
88568
  "integrations.list": {
88311
88569
  capName: "integrations",
88312
88570
  capScope: "system",
@@ -88314,13 +88572,6 @@ Object.freeze({
88314
88572
  access: "view",
88315
88573
  role: "admin"
88316
88574
  },
88317
- "integrations.setSettings": {
88318
- capName: "integrations",
88319
- capScope: "system",
88320
- addonId: null,
88321
- access: "create",
88322
- role: "admin"
88323
- },
88324
88575
  "integrations.testConnection": {
88325
88576
  capName: "integrations",
88326
88577
  capScope: "system",
@@ -89315,6 +89566,13 @@ Object.freeze({
89315
89566
  access: "view",
89316
89567
  role: "admin"
89317
89568
  },
89569
+ "migrations.clearLegacyIntegrationRows": {
89570
+ capName: "migrations",
89571
+ capScope: "system",
89572
+ addonId: null,
89573
+ access: "delete",
89574
+ role: "admin"
89575
+ },
89318
89576
  "migrations.declareOwner": {
89319
89577
  capName: "migrations",
89320
89578
  capScope: "system",
@@ -89322,6 +89580,13 @@ Object.freeze({
89322
89580
  access: "create",
89323
89581
  role: "admin"
89324
89582
  },
89583
+ "migrations.deleteLegacyIntegrationRowKeys": {
89584
+ capName: "migrations",
89585
+ capScope: "system",
89586
+ addonId: null,
89587
+ access: "delete",
89588
+ role: "admin"
89589
+ },
89325
89590
  "migrations.dryRun": {
89326
89591
  capName: "migrations",
89327
89592
  capScope: "system",
@@ -89357,6 +89622,20 @@ Object.freeze({
89357
89622
  access: "create",
89358
89623
  role: "admin"
89359
89624
  },
89625
+ "migrations.patchLegacyIntegrationRows": {
89626
+ capName: "migrations",
89627
+ capScope: "system",
89628
+ addonId: null,
89629
+ access: "create",
89630
+ role: "admin"
89631
+ },
89632
+ "migrations.readLegacyIntegrationRows": {
89633
+ capName: "migrations",
89634
+ capScope: "system",
89635
+ addonId: null,
89636
+ access: "view",
89637
+ role: "admin"
89638
+ },
89360
89639
  "migrations.restorePreMigration": {
89361
89640
  capName: "migrations",
89362
89641
  capScope: "system",