@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
@@ -6038,7 +6038,7 @@ var ZodIssueCode = {
6038
6038
  var ZodFirstPartyTypeKind;
6039
6039
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
6040
6040
  //#endregion
6041
- //#region ../types/dist/sleep-hK-Dss3z.mjs
6041
+ //#region ../types/dist/sleep-DEZaLbiu.mjs
6042
6042
  /**
6043
6043
  * The audio chunk plane's byte format, and the ONE expansion from a coded
6044
6044
  * window to float samples (D455).
@@ -6631,7 +6631,7 @@ var NOT_RUN = {
6631
6631
  * throws: an unreachable or refusing engine is logged and reported `declared: false`,
6632
6632
  * because the hub (and this addon's boot) must not hang on a migration.
6633
6633
  */
6634
- async function runAddonMigrations(host, migrations, logger) {
6634
+ async function runAddonMigrations(host, migrations, logger, options = {}) {
6635
6635
  const first = migrations[0];
6636
6636
  if (first === void 0) return NOT_RUN;
6637
6637
  const owner = first.meta.owner;
@@ -6660,7 +6660,9 @@ async function runAddonMigrations(host, migrations, logger) {
6660
6660
  else if (status === "blocked") blocked.push(id);
6661
6661
  else skipped.push(id);
6662
6662
  };
6663
+ const selected = options.only === void 0 ? null : new Set(options.only);
6663
6664
  for (const step of declaration.steps) {
6665
+ if (selected !== null && !selected.has(step.id)) continue;
6664
6666
  const bound = byId.get(step.id);
6665
6667
  if (bound === void 0 || step.action === "skip") {
6666
6668
  skipped.push(step.id);
@@ -6838,6 +6840,12 @@ function projectStore(store, perNodeKeys, nodeId) {
6838
6840
  }
6839
6841
  return out;
6840
6842
  }
6843
+ /** Every scope, in the order the guard and the docs list them. */
6844
+ var SCOPED_SETTINGS_SCOPES = [
6845
+ "cluster",
6846
+ "device-defaults",
6847
+ "node"
6848
+ ];
6841
6849
  var GLOBAL_SCOPE = { kind: "global" };
6842
6850
  /** The scope every addon-wide (not per-node, not per-device) setting lives in. */
6843
6851
  function globalSecretScope() {
@@ -7687,6 +7695,13 @@ var BaseAddon = class {
7687
7695
  */
7688
7696
  secretStatePaths = [];
7689
7697
  _addonSecretTree = null;
7698
+ /**
7699
+ * D986 M4: the scoped schemas this addon declared, resolved once per capability
7700
+ * (`loadScopedSchemas`) so the sync readers (`perNodeKeys`, the secret tree, the restart check)
7701
+ * see them. Empty until loaded: the readers then fall back to {@link globalSettingsSchema}.
7702
+ */
7703
+ _scopedByCap = /* @__PURE__ */ new Map();
7704
+ _nodeKeysByCap = /* @__PURE__ */ new Map();
7690
7705
  _deviceSecretTree = null;
7691
7706
  /** Refs the last resolved config pointed at — a change to one re-resolves. */
7692
7707
  _configSecretRefs = /* @__PURE__ */ new Set();
@@ -7756,6 +7771,7 @@ var BaseAddon = class {
7756
7771
  this._ctx = context;
7757
7772
  this._secretSettings = null;
7758
7773
  }
7774
+ await this.loadScopedSchemas();
7759
7775
  await this.resolveConfig();
7760
7776
  if (secrets !== void 0) this._secretChangeUnsub = secrets.onChange((ref) => {
7761
7777
  if (this._configSecretRefs.has(ref)) this.scheduleSecretReresolve(SECRET_CHANGE_DEBOUNCE_MS);
@@ -7765,6 +7781,7 @@ var BaseAddon = class {
7765
7781
  const normalized = normalizeAddonInitResult(result);
7766
7782
  const providers = normalized && "providers" in normalized && normalized.providers ? normalized.providers : [];
7767
7783
  this._registeredCapNames = providers.map((p) => p.capability.name);
7784
+ await this.loadScopedSchemas();
7768
7785
  return normalized;
7769
7786
  }
7770
7787
  /**
@@ -7778,14 +7795,18 @@ var BaseAddon = class {
7778
7795
  * nothing; the boot continues. A migration that FAILED (or was blocked by one)
7779
7796
  * and declares `onFailure: 'block-owner'` makes this throw, so the addon does not
7780
7797
  * serve data whose shape it cannot trust; `tolerate` migrations never do.
7798
+ *
7799
+ * ALWAYS pass the whole catalogue; a phase that must run only some of it passes
7800
+ * `{ only: [ids] }` — never a filtered catalogue (the engine would judge the
7801
+ * omitted applied ids "newer").
7781
7802
  */
7782
- async runMigrations(migrations) {
7803
+ async runMigrations(migrations, options) {
7783
7804
  const api = this.ctx.api.migrations;
7784
7805
  const report = await runAddonMigrations({
7785
7806
  declareOwner: (input) => api.declareOwner.mutate(input),
7786
7807
  startMigration: (input) => api.startMigration.mutate(input),
7787
7808
  finishMigration: (input) => api.finishMigration.mutate(input)
7788
- }, migrations, this.ctx.logger);
7809
+ }, migrations, this.ctx.logger, options);
7789
7810
  if (report.blockOwner.length > 0) throw new AddonMigrationsBlockedError(report.blockOwner);
7790
7811
  return report;
7791
7812
  }
@@ -7849,6 +7870,16 @@ var BaseAddon = class {
7849
7870
  schema(schema) {
7850
7871
  return schema;
7851
7872
  }
7873
+ /**
7874
+ * The capability names whose scoped settings schemas ({@link clusterSettingsSchema},
7875
+ * {@link nodeSettingsSchema}, {@link deviceDefaultsSchema}) this addon declares. Read BEFORE
7876
+ * `onInitialize`, when the providers it registers are not known yet: the first config resolve
7877
+ * must already know which keys are per node. Names the providers register at init are added
7878
+ * once known.
7879
+ */
7880
+ scopedSettingsCapabilities() {
7881
+ return [];
7882
+ }
7852
7883
  /** Override to provide global-level settings UI schema. */
7853
7884
  globalSettingsSchema(_cap) {
7854
7885
  return null;
@@ -7892,7 +7923,8 @@ var BaseAddon = class {
7892
7923
  return [];
7893
7924
  }
7894
7925
  async getGlobalSettings(overlay, cap, nodeId) {
7895
- const schema = this.globalSettingsSchema(cap);
7926
+ await this.loadScopedSchemas(cap);
7927
+ const schema = this.globalSettingsSchema(cap) ?? this.scopedFormSchema(cap);
7896
7928
  if (!schema) return { sections: [] };
7897
7929
  const projected = await this.readRawProjectedStore(nodeId, cap);
7898
7930
  return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(projected, overlay) : projected, { inlineSecretsAsViews: true });
@@ -7927,10 +7959,9 @@ var BaseAddon = class {
7927
7959
  *
7928
7960
  * Override it only when an `instanceMode: multiple` addon's instances differ: an NVR integration
7929
7961
  * needs its connection fields, the default integration the addon's own sections. It is the
7930
- * SHAPE of the form only — the values live in `integrations.getSettings`, and the write path is
7931
- * unchanged (`integrations.setSettings` for an account integration).
7962
+ * SHAPE of the form only — the values live on the instance record.
7932
7963
  */
7933
- async integrationSettingsSchema(_instance) {
7964
+ async integrationFormSchema(_instance) {
7934
7965
  return null;
7935
7966
  }
7936
7967
  /**
@@ -7976,7 +8007,7 @@ var BaseAddon = class {
7976
8007
  * per-instance schema when it declares one, else its global schema (what they always showed).
7977
8008
  */
7978
8009
  async getIntegrationFormSchema(instance, nodeId) {
7979
- const scoped = await this.integrationSettingsSchema(instance);
8010
+ const scoped = await this.integrationFormSchema(instance);
7980
8011
  if (scoped === null) return this.getGlobalSettings(void 0, void 0, nodeId);
7981
8012
  return hydrateSchema(scoped, {}, { inlineSecretsAsViews: true });
7982
8013
  }
@@ -8024,6 +8055,7 @@ var BaseAddon = class {
8024
8055
  * resolved blob back is a no-op for every unchanged secret.
8025
8056
  */
8026
8057
  async resolveGlobalStore(nodeId, cap) {
8058
+ await this.loadScopedSchemas(cap);
8027
8059
  const raw = await this._ctx?.settings?.readAddonStore() ?? {};
8028
8060
  return this.projectForNode(raw, nodeId, cap);
8029
8061
  }
@@ -8034,6 +8066,7 @@ var BaseAddon = class {
8034
8066
  }
8035
8067
  /** The PROJECTED store with its markers — the form hydrate only (D798). */
8036
8068
  async readRawProjectedStore(nodeId, cap) {
8069
+ await this.loadScopedSchemas(cap);
8037
8070
  const raw = await this.rawSettings()?.readAddonStore() ?? {};
8038
8071
  return this.projectForNode(raw, nodeId, cap);
8039
8072
  }
@@ -8136,6 +8169,7 @@ var BaseAddon = class {
8136
8169
  await this.applyGlobalPatch(patch, nodeId);
8137
8170
  }
8138
8171
  async applyGlobalPatch(barePatch, nodeId) {
8172
+ await this.loadScopedSchemas();
8139
8173
  const keys = this.perNodeKeys();
8140
8174
  const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
8141
8175
  const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
@@ -8149,7 +8183,8 @@ var BaseAddon = class {
8149
8183
  await this.resolveConfig();
8150
8184
  await this.onConfigChanged();
8151
8185
  this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
8152
- this.maybeAutoRestart(Object.keys(barePatch), this.globalSettingsSchema());
8186
+ const legacy = this.globalSettingsSchema();
8187
+ this.maybeAutoRestart(Object.keys(barePatch), [...legacy === null ? [] : [legacy], ...this.loadedScopedSchemas().map((entry) => entry.schema)]);
8153
8188
  }
8154
8189
  /**
8155
8190
  * The set of field keys the global settings schema declares `perNode: true`
@@ -8157,14 +8192,67 @@ var BaseAddon = class {
8157
8192
  * declarations). Empty set ⇒ every per-node code path is bypassed and the
8158
8193
  * settings API behaves exactly like the legacy node-agnostic one.
8159
8194
  */
8160
- _perNodeKeysCache = /* @__PURE__ */ new Map();
8195
+ /**
8196
+ * Load the scoped schemas of every capability this addon declares (the override and the
8197
+ * providers registered at init, plus `extraCap`) that is not loaded yet, then drop what was
8198
+ * derived from the previous set. Idempotent.
8199
+ */
8200
+ async loadScopedSchemas(extraCap) {
8201
+ const caps = new Set([
8202
+ ...this.scopedSettingsCapabilities(),
8203
+ ...this._registeredCapNames,
8204
+ ...extraCap === void 0 ? [] : [extraCap]
8205
+ ]);
8206
+ let added = false;
8207
+ for (const cap of caps) {
8208
+ if (this._scopedByCap.has(cap)) continue;
8209
+ const loaded = [];
8210
+ for (const scope of SCOPED_SETTINGS_SCOPES) {
8211
+ const schema = await this.getScopedSettingsSchema(scope, cap);
8212
+ if (schema !== null) loaded.push({
8213
+ scope,
8214
+ schema
8215
+ });
8216
+ }
8217
+ this._scopedByCap.set(cap, loaded);
8218
+ added = true;
8219
+ }
8220
+ if (!added) return;
8221
+ this._nodeKeysByCap = /* @__PURE__ */ new Map();
8222
+ this._addonSecretTree = null;
8223
+ }
8224
+ /**
8225
+ * The form the settings API serves while an addon declares no {@link globalSettingsSchema}: the
8226
+ * sections of its scoped schemas, so dropping the old hook does not blank the form.
8227
+ */
8228
+ scopedFormSchema(cap) {
8229
+ const byId = /* @__PURE__ */ new Map();
8230
+ for (const entry of this.loadedScopedSchemas(cap)) for (const section of entry.schema.sections) if (!byId.has(section.id)) byId.set(section.id, section);
8231
+ return byId.size === 0 ? null : { sections: [...byId.values()] };
8232
+ }
8233
+ /** Every loaded scoped schema of `cap`, or of every loaded capability. */
8234
+ loadedScopedSchemas(cap) {
8235
+ if (cap !== void 0) return this._scopedByCap.get(cap) ?? [];
8236
+ return [...this._scopedByCap.values()].flat();
8237
+ }
8238
+ /**
8239
+ * The field keys that are stored per node: every field of a {@link nodeSettingsSchema}, plus
8240
+ * the fields the legacy {@link globalSettingsSchema} still marks `perNode: true`. Derived once
8241
+ * per `cap` argument and memoized (schemas are static declarations). Empty set ⇒ every
8242
+ * per-node code path is bypassed and the settings API behaves like the node-agnostic one.
8243
+ * Call {@link loadScopedSchemas} first (the async seams do).
8244
+ */
8161
8245
  perNodeKeys(cap) {
8162
8246
  const cacheKey = cap ?? "";
8163
- const cached = this._perNodeKeysCache.get(cacheKey);
8247
+ const cached = this._nodeKeysByCap.get(cacheKey);
8164
8248
  if (cached) return cached;
8165
- const schema = this.globalSettingsSchema(cap);
8166
- const keys = new Set(schema ? schema.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
8167
- this._perNodeKeysCache.set(cacheKey, keys);
8249
+ const legacy = this.globalSettingsSchema(cap);
8250
+ const keys = new Set(legacy ? legacy.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
8251
+ for (const { scope, schema } of this.loadedScopedSchemas(cap)) {
8252
+ if (scope !== "node") continue;
8253
+ for (const section of schema.sections) for (const key of collectValueFieldKeys(section.fields)) keys.add(key);
8254
+ }
8255
+ this._nodeKeysByCap.set(cacheKey, keys);
8168
8256
  return keys;
8169
8257
  }
8170
8258
  /**
@@ -8174,10 +8262,9 @@ var BaseAddon = class {
8174
8262
  * time to return its response before the addon is torn down and
8175
8263
  * re-initialised by `AddonRegistryService.restartAddon`.
8176
8264
  */
8177
- maybeAutoRestart(patchKeys, schema) {
8178
- if (!schema) return;
8265
+ maybeAutoRestart(patchKeys, schemas) {
8179
8266
  const restartKeys = /* @__PURE__ */ new Set();
8180
- for (const section of schema.sections) for (const field of section.fields) {
8267
+ for (const schema of schemas) for (const section of schema.sections) for (const field of section.fields) {
8181
8268
  if (field.type === "separator" || field.type === "info") continue;
8182
8269
  if (field.requiresRestart) restartKeys.add(field.key);
8183
8270
  }
@@ -8373,12 +8460,14 @@ var BaseAddon = class {
8373
8460
  * here, in the addon's own process, so the hub keeps no copy of it.
8374
8461
  */
8375
8462
  async getDeclaredSettingsKeys() {
8463
+ await this.loadScopedSchemas();
8376
8464
  return { keys: [...this.declaredKeySet()].toSorted() };
8377
8465
  }
8378
8466
  declaredKeySet() {
8379
8467
  const keys = new Set(Object.keys(this.defaults));
8380
8468
  const schema = this.globalSettingsSchema();
8381
8469
  if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
8470
+ for (const { schema: scoped } of this.loadedScopedSchemas()) for (const section of scoped.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
8382
8471
  for (const key of this._declaredStateKeys) keys.add(key);
8383
8472
  for (const path of this.secretStatePaths) {
8384
8473
  const root = path.split(/[.[]/, 1)[0];
@@ -8474,7 +8563,11 @@ var BaseAddon = class {
8474
8563
  }
8475
8564
  /** The addon store's secret tree: schema fields by TYPE, plus `secretStatePaths`. */
8476
8565
  addonSecretTree() {
8477
- this._addonSecretTree ??= mergeSecretStatePaths(secretTreeFromSchema(this.globalSettingsSchema()), this.secretStatePaths);
8566
+ if (this._addonSecretTree === null) {
8567
+ const tree = new Map(secretTreeFromSchema(this.globalSettingsSchema()));
8568
+ for (const { schema } of this.loadedScopedSchemas()) for (const [key, node] of secretTreeFromSchema(schema)) tree.set(key, node);
8569
+ this._addonSecretTree = mergeSecretStatePaths(tree, this.secretStatePaths);
8570
+ }
8478
8571
  return this._addonSecretTree;
8479
8572
  }
8480
8573
  deviceSecretTree() {
@@ -8724,6 +8817,23 @@ function dropPerNodeFields(fields) {
8724
8817
  }
8725
8818
  return kept;
8726
8819
  }
8820
+ /** The keys of the value-holding fields, recursing into layout containers. */
8821
+ function collectValueFieldKeys(fields) {
8822
+ const collected = [];
8823
+ for (const field of fields) {
8824
+ if (field.type === "group") {
8825
+ collected.push(...collectValueFieldKeys(field.fields));
8826
+ continue;
8827
+ }
8828
+ if (field.type === "sub-tabs") {
8829
+ for (const tab of field.tabs) collected.push(...collectValueFieldKeys(tab.fields));
8830
+ continue;
8831
+ }
8832
+ if (field.type === "separator" || field.type === "info") continue;
8833
+ if ("key" in field && typeof field.key === "string") collected.push(field.key);
8834
+ }
8835
+ return collected;
8836
+ }
8727
8837
  function collectFieldKeys(fields) {
8728
8838
  const collected = [];
8729
8839
  for (const field of fields) {
@@ -8816,6 +8926,10 @@ var RECORD_SETTINGS_MAX_ROWS = 5e4;
8816
8926
  * (`collectZodSecretPaths`): dotted keys, `[]` for "each element of this array"
8817
8927
  * (`auth.accessToken`, `headers[].value`).
8818
8928
  *
8929
+ * `{}` after a key means "each value of this map" (`applianceSecrets{}`): the map's own key
8930
+ * names the value (`applianceSecrets{fridge}`), so no id key is declared and a body that drops a
8931
+ * key releases exactly that key's secret.
8932
+ *
8819
8933
  * Array elements are matched by a DECLARED id key (`secretArrayKeys`:
8820
8934
  * `{ headers: 'name' }`), never by index, so reordering a list keeps every
8821
8935
  * keychain ref (the same rule as `arrayItemFieldPath`). The concrete path of a
@@ -8828,11 +8942,13 @@ function parseSecretPath(path) {
8828
8942
  if (path.length === 0) throw new InvalidRecordError("an empty secret path");
8829
8943
  return path.split(".").map((raw) => {
8830
8944
  const each = raw.endsWith("[]");
8831
- const key = each ? raw.slice(0, -2) : raw;
8945
+ const map = raw.endsWith("{}");
8946
+ const key = each || map ? raw.slice(0, -2) : raw;
8832
8947
  if (key.length === 0) throw new InvalidRecordError(`secret path "${path}" has an empty segment`);
8833
8948
  return {
8834
8949
  key,
8835
- each
8950
+ each,
8951
+ map
8836
8952
  };
8837
8953
  });
8838
8954
  }
@@ -8919,7 +9035,8 @@ function isZodSecret(schema) {
8919
9035
  }
8920
9036
  /**
8921
9037
  * Every declared secret path in a config schema: dotted keys, `[]` for "each
8922
- * element of this array" (`connection.password`, `rows[].token`). A declared
9038
+ * element of this array" (`connection.password`, `rows[].token`), `{}` for "each value of this
9039
+ * map" (`applianceSecrets{}`). A declared
8923
9040
  * object or array is reported as one path and not descended into — everything
8924
9041
  * under it is secret.
8925
9042
  */
@@ -8930,6 +9047,7 @@ function collectZodSecretPaths(schema, prefix = "") {
8930
9047
  const def = defOf(layer);
8931
9048
  if (def.type === "object" && def.shape) for (const [key, child] of Object.entries(def.shape)) paths.push(...collectZodSecretPaths(child, prefix ? `${prefix}.${key}` : key));
8932
9049
  else if (def.type === "array" && def.element) paths.push(...collectZodSecretPaths(def.element, `${prefix}[]`));
9050
+ else if (def.type === "record" && def.valueType) paths.push(...collectZodSecretPaths(def.valueType, `${prefix}{}`));
8933
9051
  else if (def.type === "union" && def.options) for (const option of def.options) paths.push(...collectZodSecretPaths(option, prefix));
8934
9052
  else if (def.type === "intersection") {
8935
9053
  if (def.left) paths.push(...collectZodSecretPaths(def.left, prefix));
@@ -9069,7 +9187,7 @@ function defineRecordKinds(specs) {
9069
9187
  retention: spec.retention,
9070
9188
  deviceRemoval: spec.deviceRemoval,
9071
9189
  maxDataBytes: spec.maxDataBytes,
9072
- secretPaths: [...secretPaths],
9190
+ secretPaths: [...new Set([...secretPaths, ...spec.retiredSecretPaths ?? []])],
9073
9191
  secretArrayKeys: spec.secretArrayKeys === void 0 ? void 0 : { ...spec.secretArrayKeys },
9074
9192
  transactFamily: spec.transactFamily,
9075
9193
  entry: spec.entry === void 0 ? void 0 : {
@@ -11978,7 +12096,7 @@ function sleep(ms) {
11978
12096
  return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
11979
12097
  }
11980
12098
  //#endregion
11981
- //#region ../types/dist/composition-BSGHhcr8.mjs
12099
+ //#region ../types/dist/composition-pOHbTzRB.mjs
11982
12100
  /**
11983
12101
  * Deterministic SHA-256 hash of an arbitrary serialisable value. The
11984
12102
  * canonical form sorts object keys alphabetically at every depth so two
@@ -14647,7 +14765,13 @@ var MigrationLedgerEntrySchema = object({
14647
14765
  * joined by the engine from the catalogues it knows this boot. Absent when no
14648
14766
  * catalogue lists the id (a newer database).
14649
14767
  */
14650
- meta: MigrationMetaSchema.optional()
14768
+ meta: MigrationMetaSchema.optional(),
14769
+ /**
14770
+ * Set when this is an applied row of an addon owner whose catalogue the answering
14771
+ * process has not loaded (the owner has not declared yet): the row is applied, the
14772
+ * process just cannot judge it. Never `skipped-newer` — that needs the owner loaded.
14773
+ */
14774
+ ownerNotLoaded: boolean().optional()
14651
14775
  });
14652
14776
  /** A catalogued migration that is not applied, with where it stands. */
14653
14777
  var MigrationPendingEntrySchema = object({
@@ -51179,6 +51303,9 @@ var RunningProposalEvidenceSchema = object({
51179
51303
  runsFound: number().int().nonnegative(),
51180
51304
  pausesFound: number().int().nonnegative(),
51181
51305
  longestPauseMs: number().nonnegative(),
51306
+ /** Episodes shorter than ~90 s (an anti-crease pulse, a display waking): never a run, never a pause. */
51307
+ pulsesFound: number().int().nonnegative(),
51308
+ longestPulseMs: number().nonnegative(),
51182
51309
  idleFloorW: number(),
51183
51310
  noiseCeilingW: number(),
51184
51311
  plateauW: number(),
@@ -52469,7 +52596,27 @@ method(object({
52469
52596
  * exposes only the COMMON surface: link state, supported device
52470
52597
  * kinds (so the UI can filter the device picker), and per-device
52471
52598
  * expose/unexpose.
52472
- */
52599
+ *
52600
+ * INSTANCES. One export addon can serve several independent targets (a
52601
+ * HomeKit bridge, a Home Assistant broker). `listInstances` names them.
52602
+ * `instanceId` on a READ is optional: absent = the whole addon (linked if any
52603
+ * enabled instance is, `exposedDeviceCount` = distinct devices, `setup` = the
52604
+ * default instance's); present = that instance; an id the provider does not
52605
+ * serve is refused with `ExportInstanceUnknownError` (`export-instance-unknown`),
52606
+ * never answered empty. `instanceId` on a WRITE is REQUIRED: a membership
52607
+ * change always names the target it changes. A single-instance provider
52608
+ * accepts an absent id on a read or its own id, and refuses any other.
52609
+ */
52610
+ /** Which instance of an export addon a call addresses. Absent on a read = the whole addon. */
52611
+ var InstanceIdSchema$1 = string().min(1);
52612
+ var InstancePinSchema = object({ instanceId: InstanceIdSchema$1.optional() });
52613
+ /** One independent export target of an addon. */
52614
+ var ExportInstanceSchema = object({
52615
+ instanceId: InstanceIdSchema$1,
52616
+ name: string(),
52617
+ enabled: boolean(),
52618
+ kind: string().optional()
52619
+ });
52473
52620
  var LinkStateSchema = _enum([
52474
52621
  "unlinked",
52475
52622
  "linked",
@@ -52520,10 +52667,14 @@ var ExposedDeviceSchema = object({
52520
52667
  capabilities: array(string()).optional()
52521
52668
  });
52522
52669
  var ExposeInputSchema = object({
52670
+ instanceId: InstanceIdSchema$1,
52523
52671
  deviceId: string(),
52524
52672
  capabilities: array(string()).optional()
52525
52673
  });
52526
- var UnexposeInputSchema = object({ deviceId: string() });
52674
+ var UnexposeInputSchema = object({
52675
+ instanceId: InstanceIdSchema$1,
52676
+ deviceId: string()
52677
+ });
52527
52678
  /**
52528
52679
  * What an exporter may expose besides devices (D968). An action and a
52529
52680
  * notification target are LIST ENTRIES, never devices (D954): they enter an
@@ -52552,15 +52703,21 @@ var ExposedEntitySchema = object({
52552
52703
  entity: ExportEntityRefSchema,
52553
52704
  exposedAs: string().optional()
52554
52705
  });
52555
- var ExposeEntityInputSchema = object({ entity: ExportEntityRefSchema });
52556
- var UnexposeEntityInputSchema = object({ entity: ExportEntityRefSchema });
52557
- method(_void(), DeviceExportStatusSchema), method(_void(), array(DeviceKindSchema)), method(_void(), array(ExposedDeviceSchema)), method(ExposeInputSchema, _void(), {
52706
+ var ExposeEntityInputSchema = object({
52707
+ instanceId: InstanceIdSchema$1,
52708
+ entity: ExportEntityRefSchema
52709
+ });
52710
+ var UnexposeEntityInputSchema = object({
52711
+ instanceId: InstanceIdSchema$1,
52712
+ entity: ExportEntityRefSchema
52713
+ });
52714
+ method(InstancePinSchema, DeviceExportStatusSchema), method(_void(), array(ExportInstanceSchema)), method(InstancePinSchema, array(DeviceKindSchema)), method(InstancePinSchema, array(ExposedDeviceSchema)), method(ExposeInputSchema, _void(), {
52558
52715
  kind: "mutation",
52559
52716
  auth: "admin"
52560
52717
  }), method(UnexposeInputSchema, _void(), {
52561
52718
  kind: "mutation",
52562
52719
  auth: "admin"
52563
- }), optionalMethod(_void(), array(ExportEntityKindSchema)), optionalMethod(_void(), array(ExposedEntitySchema)), optionalMethod(ExposeEntityInputSchema, _void(), {
52720
+ }), optionalMethod(_void(), array(ExportEntityKindSchema)), optionalMethod(InstancePinSchema, array(ExposedEntitySchema)), optionalMethod(ExposeEntityInputSchema, _void(), {
52564
52721
  kind: "mutation",
52565
52722
  auth: "admin"
52566
52723
  }), optionalMethod(UnexposeEntityInputSchema, _void(), {
@@ -57061,11 +57218,7 @@ var CreateIntegrationInputSchema = object({
57061
57218
  info: record(string(), unknown()).optional(),
57062
57219
  /** `stableId` of the discovery candidate the form was seeded from (D984). */
57063
57220
  originStableId: string().optional(),
57064
- settings: record(string(), unknown()).optional(),
57065
- /** Keys of `settings` the form's schema declares secret (by type) — kept in
57066
- * the keychain, not the table (D787). */
57067
- secretKeys: array(string()).optional(),
57068
- skipRestart: boolean().optional()
57221
+ settings: record(string(), unknown()).optional()
57069
57222
  });
57070
57223
  var UpdateIntegrationInputSchema = object({
57071
57224
  id: string(),
@@ -57086,16 +57239,6 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
57086
57239
  }), method(object({ id: string() }), DeleteIntegrationResultSchema, {
57087
57240
  kind: "mutation",
57088
57241
  auth: "admin"
57089
- }), method(object({ id: string() }), record(string(), unknown()), { auth: "admin" }), method(object({
57090
- id: string(),
57091
- settings: record(string(), unknown()),
57092
- /** Keys the form's schema declares secret (by type) — keychain-stored (D787). */
57093
- secretKeys: array(string()).optional(),
57094
- /** The integration's own addon applies the change in-process and asks the hub not to respawn it. */
57095
- skipRestart: boolean().optional()
57096
- }), object({ success: boolean() }), {
57097
- kind: "mutation",
57098
- auth: "admin"
57099
57242
  }), method(_void(), array(AvailableIntegrationTypeSchema), { auth: "admin" }), method(object({
57100
57243
  addonId: string(),
57101
57244
  settings: record(string(), unknown())
@@ -57124,13 +57267,65 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
57124
57267
  */
57125
57268
  var ConfigSchemaPassthrough$3 = custom();
57126
57269
  var IntegrationInstanceIdSchema = string().min(1);
57270
+ var IntegrationInstanceModeSchema = _enum(["single", "multiple"]);
57271
+ /**
57272
+ * One kind of instance a provider can create (an adapter, an account type). The sign-in and
57273
+ * connect fields follow the `broker` contract (`broker.cap.ts`): `signIn.basePath` is a
57274
+ * hub-relative route set speaking the sign-in contract, `connectPath` / `disconnectPath` open an
57275
+ * interactive account link (popup) and may carry `{{brokerId}}`, which the UI replaces with the
57276
+ * instance id.
57277
+ */
57278
+ var IntegrationInstanceKindSchema = object({
57279
+ /** Stable id of the kind, sent back as `createInstance.kind`. */
57280
+ id: string().min(1),
57281
+ label: string().min(1),
57282
+ /** The kind signs in through a server-held interactive session. */
57283
+ signIn: object({
57284
+ basePath: string().min(1),
57285
+ methods: array(_enum(["device-code", "proxy"])).optional()
57286
+ }).optional(),
57287
+ /** Relative hub path to start an interactive account link of an instance of this kind. */
57288
+ connectPath: string().min(1).optional(),
57289
+ /** Relative hub path to disconnect that account. */
57290
+ disconnectPath: string().min(1).optional()
57291
+ });
57127
57292
  var IntegrationInstanceDescriptionSchema = object({
57293
+ /** What the instances are for the Integrations page; see {@link IntegrationInstanceRoleSchema}. */
57294
+ role: _enum([
57295
+ "devices",
57296
+ "services",
57297
+ "both"
57298
+ ]),
57299
+ /** How the devices of an instance are found; absent means `{ mode: 'none' }`. */
57300
+ discovery: object({
57301
+ mode: _enum([
57302
+ "none",
57303
+ "lan",
57304
+ "account"
57305
+ ]),
57306
+ scopedToInstance: boolean()
57307
+ }).optional(),
57128
57308
  /** `single`: at most one instance can exist (Loki). `multiple`: an account each. */
57129
- instanceMode: _enum(["single", "multiple"]),
57309
+ instanceMode: IntegrationInstanceModeSchema,
57130
57310
  /** What the instance is, for the UI (`destination`, `relay`, `account`). */
57131
57311
  kind: string(),
57132
57312
  /** True when `testInstance` really probes the remote. */
57133
- canTest: boolean()
57313
+ canTest: boolean(),
57314
+ /**
57315
+ * The kinds an instance can be of. Absent or one entry: nothing to choose; more than one: the
57316
+ * Add flow asks which first and sends it as `createInstance.kind`.
57317
+ */
57318
+ kinds: array(IntegrationInstanceKindSchema).optional(),
57319
+ /**
57320
+ * The provider accepts `createInstance.id` for an id that once existed (an adopted id, because
57321
+ * devices carry it). The UI then offers "restore under its old id" on Add. Absent means no.
57322
+ */
57323
+ allowRestoreId: boolean().optional(),
57324
+ /**
57325
+ * `createInstance` without an `id` works: the provider mints it. Absent means the caller must
57326
+ * send an id (the UI then mints an opaque one).
57327
+ */
57328
+ mintsId: boolean().optional()
57134
57329
  });
57135
57330
  var IntegrationInstanceStatusSchema = object({
57136
57331
  state: _enum([
@@ -57139,7 +57334,9 @@ var IntegrationInstanceStatusSchema = object({
57139
57334
  "error",
57140
57335
  "unknown"
57141
57336
  ]),
57142
- detail: string().optional()
57337
+ detail: string().optional(),
57338
+ /** A short human word for the state ("Signed in", "Token expired"); shown instead of `state`. */
57339
+ label: string().optional()
57143
57340
  });
57144
57341
  var IntegrationInstanceSchema = object({
57145
57342
  id: IntegrationInstanceIdSchema,
@@ -57147,13 +57344,18 @@ var IntegrationInstanceSchema = object({
57147
57344
  /** The addon's own rule (an URL is present, a client id is present...). */
57148
57345
  configured: boolean(),
57149
57346
  /**
57150
- * The instance's own on/off switch (D996): always present, derived from the record body
57151
- * (`enabled !== false`, so a row written before the field existed is on).
57347
+ * The instance's own on/off switch (D996), derived from the record body (`enabled !== false`,
57348
+ * so a row written before the field existed is on). Absent when the `integrations` registry
57349
+ * owns the switch (`describe().role` is `devices`): read it from the registry row.
57152
57350
  */
57153
- enabled: boolean(),
57351
+ enabled: boolean().optional(),
57352
+ /** The kind of this instance (one of `describe().kinds[].id`), when the provider has kinds. */
57353
+ kind: string().optional(),
57154
57354
  /** Values for the instance form; a secret is a `SecretFieldView`, never text. */
57155
57355
  formValues: record(string(), unknown()),
57156
- status: IntegrationInstanceStatusSchema.optional()
57356
+ status: IntegrationInstanceStatusSchema.optional(),
57357
+ /** Epoch ms of the last probe of the remote that produced `status`; absent when never checked. */
57358
+ lastCheckedAt: number().optional()
57157
57359
  });
57158
57360
  var IntegrationInstancePatchSchema = object({
57159
57361
  id: IntegrationInstanceIdSchema,
@@ -57162,8 +57364,9 @@ var IntegrationInstancePatchSchema = object({
57162
57364
  values: record(string(), unknown())
57163
57365
  });
57164
57366
  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({
57165
- id: IntegrationInstanceIdSchema,
57166
- name: string().min(1)
57367
+ id: IntegrationInstanceIdSchema.optional(),
57368
+ name: string().min(1),
57369
+ kind: string().min(1).optional()
57167
57370
  }), object({ id: string() }), {
57168
57371
  kind: "mutation",
57169
57372
  auth: "admin"
@@ -59323,6 +59526,11 @@ var LogEntrySchema = object({
59323
59526
  * flight, and not asking for the next slice ends the scan.
59324
59527
  */
59325
59528
  var LogReadQuerySchema = object({
59529
+ /**
59530
+ * Which destination INSTANCE answers (a destination addon may hold several records). Absent ⇒
59531
+ * the provider's first readable one, so a caller written before instances existed is unchanged.
59532
+ */
59533
+ instanceId: string().min(1).max(200).optional(),
59326
59534
  /** Correlates `cancelRead`. Caller-chosen, unique per in-flight read. */
59327
59535
  requestId: string().min(1).max(64).optional(),
59328
59536
  /** The device the line is about (`tags.deviceId`, numeric). */
@@ -59385,6 +59593,8 @@ var LogReadResultSchema = object({
59385
59593
  /** True when `cancelRead` ended this slice early. */
59386
59594
  cancelled: boolean().optional()
59387
59595
  });
59596
+ /** Names one destination instance; absent ⇒ the provider's first readable one. */
59597
+ var LogInstanceSelectorSchema = object({ instanceId: string().min(1).max(200).optional() });
59388
59598
  var LogReadCapabilitiesSchema = object({
59389
59599
  /** False ⇒ this destination only ships lines (console, forwarder, an unconfigured Loki). */
59390
59600
  readable: boolean(),
@@ -59403,7 +59613,7 @@ var LogReadCapabilitiesSchema = object({
59403
59613
  /** How far back the store reaches, when the provider knows (days). */
59404
59614
  retentionDays: number().nullable()
59405
59615
  });
59406
- 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() }), {
59616
+ 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() }), {
59407
59617
  kind: "mutation",
59408
59618
  auth: "admin"
59409
59619
  });
@@ -60100,6 +60310,27 @@ method(object({}), object({ entries: array(MigrationLedgerEntrySchema) }), { aut
60100
60310
  }), {
60101
60311
  kind: "mutation",
60102
60312
  auth: "admin"
60313
+ }), method(object({ integrationId: string().min(1) }), record(string(), unknown()), { auth: "admin" }), method(object({ integrationId: string().min(1) }), object({
60314
+ rows: number().int().nonnegative(),
60315
+ secrets: number().int().nonnegative()
60316
+ }), {
60317
+ kind: "mutation",
60318
+ auth: "admin"
60319
+ }), method(object({
60320
+ integrationId: string().min(1),
60321
+ patch: record(string(), unknown())
60322
+ }), object({ written: boolean() }), {
60323
+ kind: "mutation",
60324
+ auth: "admin"
60325
+ }), method(object({
60326
+ integrationId: string().min(1),
60327
+ keys: array(string().min(1))
60328
+ }), object({
60329
+ present: boolean(),
60330
+ removed: record(string(), unknown())
60331
+ }), {
60332
+ kind: "mutation",
60333
+ auth: "admin"
60103
60334
  });
60104
60335
  method(object({
60105
60336
  sourceUrl: string(),
@@ -83245,6 +83476,25 @@ function buildEventKindDescriptor(kind, source) {
83245
83476
  }
83246
83477
  };
83247
83478
  }
83479
+ /**
83480
+ * What a remote key is CALLED and what it LOOKS like, for every role (D963),
83481
+ * shared by the admin UI and the viewer. A role is data on the wire; each
83482
+ * client resolves its name from `label` (English and Italian side by side) and
83483
+ * its glyph from `icon`, a lucide id from the shared icon catalogue — the
83484
+ * server sends an id, a client maps it to its own icon set.
83485
+ *
83486
+ * `legend` is text printed on the key INSTEAD of a glyph (a digit has no
83487
+ * pictogram): a client that can draw text shows it, one that cannot falls back
83488
+ * to `icon`.
83489
+ */
83490
+ function digit(n) {
83491
+ return {
83492
+ label: t(n, n),
83493
+ icon: "hash",
83494
+ legend: n
83495
+ };
83496
+ }
83497
+ digit("0"), digit("1"), digit("2"), digit("3"), digit("4"), digit("5"), digit("6"), digit("7"), digit("8"), digit("9");
83248
83498
  DeviceFeature.MediaPlayerPower, DeviceFeature.MediaPlayerVolumeStep, DeviceFeature.MediaPlayerChannelStep, DeviceFeature.MediaPlayerMute, DeviceFeature.MediaPlayerStop, DeviceFeature.MediaPlayerNext, DeviceFeature.MediaPlayerPrevious;
83249
83499
  var btn = (idSuffix, row, col, init) => ({
83250
83500
  idSuffix,
@@ -83383,11 +83633,15 @@ discriminatedUnion("kind", [object({
83383
83633
  * 1. idle floor F = time-weighted median; noise ceiling N = highest value seen
83384
83634
  * near F. A device that is not near one level most of the time has no floor.
83385
83635
  * 2. coarse activity = above max(2N, N + 5 W); consecutive stretches are episodes.
83386
- * 3. gaps between episodes split in two by the largest log-scale jump (>= 3x):
83387
- * the short ones are pauses INSIDE a run (a soak), the long ones separate runs.
83636
+ * 3. an episode shorter than `PULSE_MAX_MS` is a PULSE (an anti-crease tumble, a
83637
+ * display waking): it is neither a run nor a pause. Among the remaining
83638
+ * episodes, gaps over `PAUSE_MAX_MS` always separate runs; the rest split in
83639
+ * two by the largest log-scale jump (>= 3x): the short ones are pauses INSIDE
83640
+ * a run (a soak), the long ones separate runs. A stretch under `MIN_RUN_MS`
83641
+ * is no run.
83388
83642
  * 4. T = geometric mean of N and the plateau's 10th percentile, snapped to a
83389
- * grid a person would type; stop dwell covers the longest pause; start dwell
83390
- * covers the longest standby blip above T.
83643
+ * grid a person would type; stop dwell covers the longest pause and the
83644
+ * kind's floor; start dwell covers the longest pulse and standby blip above T.
83391
83645
  * 5. the numbers are proved by REPLAY through the real `dwell` evaluation: each
83392
83646
  * run must hold exactly one true interval and nothing may flap outside.
83393
83647
  */
@@ -83398,6 +83652,10 @@ var MINUTE_MS$1 = 60 * SECOND_MS;
83398
83652
  30 * SECOND_MS;
83399
83653
  60 * SECOND_MS;
83400
83654
  20 * MINUTE_MS$1;
83655
+ 90 * SECOND_MS;
83656
+ 30 * MINUTE_MS$1;
83657
+ 10 * MINUTE_MS$1;
83658
+ DeviceType.Dryer, 3 * MINUTE_MS$1, DeviceType.Dishwasher, 3 * MINUTE_MS$1, DeviceType.Washer, 10 * MINUTE_MS$1;
83401
83659
  10 * SECOND_MS;
83402
83660
  10 * SECOND_MS;
83403
83661
  DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverStoppable, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
@@ -86582,6 +86840,13 @@ Object.freeze({
86582
86840
  access: "view",
86583
86841
  role: "admin"
86584
86842
  },
86843
+ "deviceExport.listInstances": {
86844
+ capName: "device-export",
86845
+ capScope: "system",
86846
+ addonId: null,
86847
+ access: "view",
86848
+ role: "admin"
86849
+ },
86585
86850
  "deviceExport.listSupportedDeviceKinds": {
86586
86851
  capName: "device-export",
86587
86852
  capScope: "system",
@@ -88269,13 +88534,6 @@ Object.freeze({
88269
88534
  access: "view",
88270
88535
  role: "admin"
88271
88536
  },
88272
- "integrations.getSettings": {
88273
- capName: "integrations",
88274
- capScope: "system",
88275
- addonId: null,
88276
- access: "view",
88277
- role: "admin"
88278
- },
88279
88537
  "integrations.list": {
88280
88538
  capName: "integrations",
88281
88539
  capScope: "system",
@@ -88283,13 +88541,6 @@ Object.freeze({
88283
88541
  access: "view",
88284
88542
  role: "admin"
88285
88543
  },
88286
- "integrations.setSettings": {
88287
- capName: "integrations",
88288
- capScope: "system",
88289
- addonId: null,
88290
- access: "create",
88291
- role: "admin"
88292
- },
88293
88544
  "integrations.testConnection": {
88294
88545
  capName: "integrations",
88295
88546
  capScope: "system",
@@ -89284,6 +89535,13 @@ Object.freeze({
89284
89535
  access: "view",
89285
89536
  role: "admin"
89286
89537
  },
89538
+ "migrations.clearLegacyIntegrationRows": {
89539
+ capName: "migrations",
89540
+ capScope: "system",
89541
+ addonId: null,
89542
+ access: "delete",
89543
+ role: "admin"
89544
+ },
89287
89545
  "migrations.declareOwner": {
89288
89546
  capName: "migrations",
89289
89547
  capScope: "system",
@@ -89291,6 +89549,13 @@ Object.freeze({
89291
89549
  access: "create",
89292
89550
  role: "admin"
89293
89551
  },
89552
+ "migrations.deleteLegacyIntegrationRowKeys": {
89553
+ capName: "migrations",
89554
+ capScope: "system",
89555
+ addonId: null,
89556
+ access: "delete",
89557
+ role: "admin"
89558
+ },
89294
89559
  "migrations.dryRun": {
89295
89560
  capName: "migrations",
89296
89561
  capScope: "system",
@@ -89326,6 +89591,20 @@ Object.freeze({
89326
89591
  access: "create",
89327
89592
  role: "admin"
89328
89593
  },
89594
+ "migrations.patchLegacyIntegrationRows": {
89595
+ capName: "migrations",
89596
+ capScope: "system",
89597
+ addonId: null,
89598
+ access: "create",
89599
+ role: "admin"
89600
+ },
89601
+ "migrations.readLegacyIntegrationRows": {
89602
+ capName: "migrations",
89603
+ capScope: "system",
89604
+ addonId: null,
89605
+ access: "view",
89606
+ role: "admin"
89607
+ },
89329
89608
  "migrations.restorePreMigration": {
89330
89609
  capName: "migrations",
89331
89610
  capScope: "system",