openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
527 lines (526 loc) • 23.1 kB
JavaScript
import { c as normalizeOptionalString } from "./string-coerce-mnp54Vah.js";
import { y as resolveStateDir } from "./paths-mvMm5bYV.js";
import { n as normalizeAccountId } from "./account-id-Df9e41E6.js";
import { t as loadJsonFile } from "./json-store-CWaMsrLM.js";
import "./string-coerce-runtime-CEGJWkQ_.js";
import "./state-paths-DzVdErem.js";
import { a as resolveMatrixDefaultOrOnlyAccountId, i as resolveMatrixChannelConfig, n as requiresExplicitMatrixDefaultAccount, o as resolveMatrixAccountStringValues, r as resolveConfiguredMatrixAccountIds, t as findMatrixAccountEntry } from "./account-selection-CqvkLWBU.js";
import { i as resolveScopedMatrixEnvConfig, n as resolveGlobalMatrixEnvConfig } from "./env-auth-B_5QIHM9.js";
import { a as resolveMatrixCredentialsPath, n as resolveMatrixAccountStorageRoot, s as resolveMatrixLegacyFlatStoragePaths } from "./storage-paths-BnIxy2QZ.js";
import { t as formatMatrixErrorMessage } from "./errors-C4iaVh6O.js";
import { C as writeMatrixLegacyCryptoMigrationState, T as writeMatrixRecoveryKeyState, _ as readMatrixLegacyCryptoMigrationState, l as migrateLegacyMatrixRecoveryKeyFileToStore, s as migrateLegacyMatrixLegacyCryptoMigrationFileToStore, v as readMatrixRecoveryKeyState } from "./crypto-state-store-Dh3bqPTW.js";
import "./migration-snapshot-backup-B3kTguEe.js";
import { fileURLToPath } from "node:url";
import fs from "node:fs";
import path from "node:path";
import os from "node:os";
//#region extensions/matrix/src/migration-config.ts
function clean(value) {
return normalizeOptionalString(value) ?? "";
}
function resolveMatrixAccountConfigEntry(cfg, accountId) {
return findMatrixAccountEntry(cfg, accountId);
}
function resolveMatrixFlatStoreSelectionNote(cfg, accountId) {
if (resolveConfiguredMatrixAccountIds(cfg).length <= 1) return;
return `Legacy Matrix flat store uses one shared on-disk state, so it will be migrated into account "${accountId}".`;
}
function resolveMatrixMigrationConfigFields(params) {
const channel = resolveMatrixChannelConfig(params.cfg);
const account = resolveMatrixAccountConfigEntry(params.cfg, params.accountId);
const scopedEnv = resolveScopedMatrixEnvConfig(params.accountId, params.env);
const globalEnv = resolveGlobalMatrixEnvConfig(params.env);
const resolvedStrings = resolveMatrixAccountStringValues({
accountId: normalizeAccountId(params.accountId),
account: {
homeserver: clean(account?.homeserver),
userId: clean(account?.userId),
accessToken: clean(account?.accessToken)
},
scopedEnv,
channel: {
homeserver: clean(channel?.homeserver),
userId: clean(channel?.userId),
accessToken: clean(channel?.accessToken)
},
globalEnv
});
return {
homeserver: resolvedStrings.homeserver,
userId: resolvedStrings.userId,
accessToken: resolvedStrings.accessToken
};
}
function loadStoredMatrixCredentials(env, accountId) {
const credentialsPath = resolveMatrixCredentialsPath({
stateDir: resolveStateDir(env, os.homedir),
accountId: normalizeAccountId(accountId)
});
try {
if (!fs.existsSync(credentialsPath)) return null;
const parsed = JSON.parse(fs.readFileSync(credentialsPath, "utf8"));
if (typeof parsed.homeserver !== "string" || typeof parsed.userId !== "string" || typeof parsed.accessToken !== "string") return null;
return {
homeserver: parsed.homeserver,
userId: parsed.userId,
accessToken: parsed.accessToken,
deviceId: typeof parsed.deviceId === "string" ? parsed.deviceId : void 0
};
} catch {
return null;
}
}
function credentialsMatchResolvedIdentity(stored, identity) {
if (!stored || !identity.homeserver) return false;
if (!identity.userId) {
if (!identity.accessToken) return false;
return stored.homeserver === identity.homeserver && stored.accessToken === identity.accessToken;
}
return stored.homeserver === identity.homeserver && stored.userId === identity.userId;
}
function resolveMatrixMigrationAccountTarget(params) {
const stored = loadStoredMatrixCredentials(params.env, params.accountId);
const resolved = resolveMatrixMigrationConfigFields(params);
const matchingStored = credentialsMatchResolvedIdentity(stored, {
homeserver: resolved.homeserver,
userId: resolved.userId,
accessToken: resolved.accessToken
}) ? stored : null;
const homeserver = resolved.homeserver;
const userId = resolved.userId || matchingStored?.userId || "";
const accessToken = resolved.accessToken || matchingStored?.accessToken || "";
if (!homeserver || !userId || !accessToken) return null;
const { rootDir } = resolveMatrixAccountStorageRoot({
stateDir: resolveStateDir(params.env, os.homedir),
homeserver,
userId,
accessToken,
accountId: params.accountId
});
return {
accountId: params.accountId,
homeserver,
userId,
accessToken,
rootDir,
storedDeviceId: matchingStored?.deviceId ?? null
};
}
function resolveLegacyMatrixFlatStoreTarget(params) {
if (!resolveMatrixChannelConfig(params.cfg)) return { warning: `Legacy Matrix ${params.detectedKind} detected at ${params.detectedPath}, but channels.matrix is not configured yet. Configure Matrix, then rerun "openclaw doctor --fix" or restart the gateway.` };
if (requiresExplicitMatrixDefaultAccount(params.cfg)) return { warning: `Legacy Matrix ${params.detectedKind} detected at ${params.detectedPath}, but multiple Matrix accounts are configured and channels.matrix.defaultAccount is not set. Set "channels.matrix.defaultAccount" to the intended target account before rerunning "openclaw doctor --fix" or restarting the gateway.` };
const accountId = resolveMatrixDefaultOrOnlyAccountId(params.cfg);
const target = resolveMatrixMigrationAccountTarget({
cfg: params.cfg,
env: params.env,
accountId
});
if (!target) {
const targetDescription = params.detectedKind === "state" ? "the new account-scoped target" : "the account-scoped target";
return { warning: `Legacy Matrix ${params.detectedKind} detected at ${params.detectedPath}, but ${targetDescription} could not be resolved yet (need homeserver, userId, and access token for channels.matrix${accountId === "default" ? "" : `.accounts.${accountId}`}). Start the gateway once with a working Matrix login, or rerun "openclaw doctor --fix" after cached credentials are available.` };
}
return {
...target,
selectionNote: resolveMatrixFlatStoreSelectionNote(params.cfg, accountId)
};
}
//#endregion
//#region extensions/matrix/src/legacy-state.ts
function resolveLegacyMatrixPaths(env) {
return resolveMatrixLegacyFlatStoragePaths(resolveStateDir(env, os.homedir));
}
function resolveMatrixMigrationPlan(params) {
const legacy = resolveLegacyMatrixPaths(params.env);
if (!fs.existsSync(legacy.storagePath) && !fs.existsSync(legacy.cryptoPath)) return null;
const target = resolveLegacyMatrixFlatStoreTarget({
cfg: params.cfg,
env: params.env,
detectedPath: legacy.rootDir,
detectedKind: "state"
});
if ("warning" in target) return target;
return {
accountId: target.accountId,
legacyStoragePath: legacy.storagePath,
legacyCryptoPath: legacy.cryptoPath,
targetRootDir: target.rootDir,
targetStoragePath: path.join(target.rootDir, "bot-storage.json"),
targetCryptoPath: path.join(target.rootDir, "crypto"),
selectionNote: target.selectionNote
};
}
function detectLegacyMatrixState(params) {
return resolveMatrixMigrationPlan({
cfg: params.cfg,
env: params.env ?? process.env
});
}
function moveLegacyPath(params) {
if (!fs.existsSync(params.sourcePath)) return;
if (fs.existsSync(params.targetPath)) {
params.warnings.push(`Matrix legacy ${params.label} not migrated because the target already exists (${params.targetPath}).`);
return;
}
try {
fs.mkdirSync(path.dirname(params.targetPath), { recursive: true });
fs.renameSync(params.sourcePath, params.targetPath);
params.changes.push(`Migrated Matrix legacy ${params.label}: ${params.sourcePath} -> ${params.targetPath}`);
} catch (err) {
params.warnings.push(`Failed migrating Matrix legacy ${params.label} (${params.sourcePath} -> ${params.targetPath}): ${String(err)}`);
}
}
async function autoMigrateLegacyMatrixState(params) {
const env = params.env ?? process.env;
const detection = detectLegacyMatrixState({
cfg: params.cfg,
env
});
if (!detection) return {
migrated: false,
changes: [],
warnings: []
};
if ("warning" in detection) {
params.log?.warn?.(`matrix: ${detection.warning}`);
return {
migrated: false,
changes: [],
warnings: [detection.warning]
};
}
const changes = [];
const warnings = [];
moveLegacyPath({
sourcePath: detection.legacyStoragePath,
targetPath: detection.targetStoragePath,
label: "sync store",
changes,
warnings
});
moveLegacyPath({
sourcePath: detection.legacyCryptoPath,
targetPath: detection.targetCryptoPath,
label: "crypto store",
changes,
warnings
});
if (changes.length > 0) {
const details = [
...changes.map((entry) => `- ${entry}`),
...detection.selectionNote ? [`- ${detection.selectionNote}`] : [],
"- No user action required."
];
params.log?.info?.(`matrix: plugin upgraded in place for account "${detection.accountId}".\n${details.join("\n")}`);
}
if (warnings.length > 0) params.log?.warn?.(`matrix: legacy state migration warnings:\n${warnings.map((entry) => `- ${entry}`).join("\n")}`);
return {
migrated: changes.length > 0,
changes,
warnings
};
}
//#endregion
//#region extensions/matrix/src/legacy-crypto-inspector-availability.ts
const LEGACY_CRYPTO_INSPECTOR_FILE = "legacy-crypto-inspector.js";
const LEGACY_CRYPTO_INSPECTOR_CHUNK_PREFIX = "legacy-crypto-inspector-";
const LEGACY_CRYPTO_INSPECTOR_HELPER_CHUNK_PREFIX = "availability-";
const JAVASCRIPT_MODULE_SUFFIX = ".js";
function isLegacyCryptoInspectorArtifactName(name) {
if (name === LEGACY_CRYPTO_INSPECTOR_FILE) return true;
if (!name.startsWith(LEGACY_CRYPTO_INSPECTOR_CHUNK_PREFIX) || !name.endsWith(JAVASCRIPT_MODULE_SUFFIX)) return false;
const chunkSuffix = name.slice(24, -3);
return chunkSuffix.length > 0 && chunkSuffix !== "availability" && !chunkSuffix.startsWith(LEGACY_CRYPTO_INSPECTOR_HELPER_CHUNK_PREFIX);
}
function hasSourceInspectorArtifact(currentDir) {
return [path.resolve(currentDir, "matrix", "legacy-crypto-inspector.ts"), path.resolve(currentDir, "matrix", "legacy-crypto-inspector.js")].some((candidate) => fs.existsSync(candidate));
}
function hasBuiltInspectorArtifact(currentDir) {
if (fs.existsSync(path.join(currentDir, "legacy-crypto-inspector.js"))) return true;
if (fs.existsSync(path.join(currentDir, "extensions", "matrix", "legacy-crypto-inspector.js"))) return true;
return fs.readdirSync(currentDir, { withFileTypes: true }).some((entry) => entry.isFile() && isLegacyCryptoInspectorArtifactName(entry.name));
}
function isMatrixLegacyCryptoInspectorAvailable() {
const currentDir = path.dirname(fileURLToPath(import.meta.url));
if (hasSourceInspectorArtifact(currentDir)) return true;
try {
return hasBuiltInspectorArtifact(currentDir);
} catch {
return false;
}
}
//#endregion
//#region extensions/matrix/src/legacy-crypto.ts
const MATRIX_LEGACY_CRYPTO_INSPECTOR_UNAVAILABLE_MESSAGE = "Legacy Matrix encrypted state was detected, but the Matrix crypto inspector is unavailable.";
async function loadMatrixLegacyCryptoInspector() {
return (await import("./legacy-crypto-inspector-DDe1FGQX.js")).inspectLegacyMatrixCryptoStore;
}
function detectLegacyBotSdkCryptoStore(cryptoRootDir) {
try {
if (!fs.statSync(cryptoRootDir).isDirectory()) return {
detected: false,
warning: `Legacy Matrix encrypted state path exists but is not a directory: ${cryptoRootDir}. OpenClaw skipped automatic crypto migration for that path.`
};
} catch (err) {
return {
detected: false,
warning: `Failed reading legacy Matrix encrypted state path (${cryptoRootDir}): ${String(err)}. OpenClaw skipped automatic crypto migration for that path.`
};
}
try {
return { detected: fs.existsSync(path.join(cryptoRootDir, "bot-sdk.json")) || fs.existsSync(path.join(cryptoRootDir, "matrix-sdk-crypto.sqlite3")) || fs.readdirSync(cryptoRootDir, { withFileTypes: true }).some((entry) => entry.isDirectory() && fs.existsSync(path.join(cryptoRootDir, entry.name, "matrix-sdk-crypto.sqlite3"))) };
} catch (err) {
return {
detected: false,
warning: `Failed scanning legacy Matrix encrypted state path (${cryptoRootDir}): ${String(err)}. OpenClaw skipped automatic crypto migration for that path.`
};
}
}
function resolveMatrixAccountIds(cfg) {
return resolveConfiguredMatrixAccountIds(cfg);
}
function resolveLegacyMatrixFlatStorePlan(params) {
const legacy = resolveMatrixLegacyFlatStoragePaths(resolveStateDir(params.env, os.homedir));
if (!fs.existsSync(legacy.cryptoPath)) return null;
const legacyStore = detectLegacyBotSdkCryptoStore(legacy.cryptoPath);
if (legacyStore.warning) return { warning: legacyStore.warning };
if (!legacyStore.detected) return null;
const target = resolveLegacyMatrixFlatStoreTarget({
cfg: params.cfg,
env: params.env,
detectedPath: legacy.cryptoPath,
detectedKind: "encrypted state"
});
if ("warning" in target) return target;
const metadata = loadLegacyBotSdkMetadata(legacy.cryptoPath);
return {
accountId: target.accountId,
rootDir: target.rootDir,
recoveryKeyPath: path.join(target.rootDir, "recovery-key.json"),
statePath: path.join(target.rootDir, "legacy-crypto-migration.json"),
legacyCryptoPath: legacy.cryptoPath,
homeserver: target.homeserver,
userId: target.userId,
accessToken: target.accessToken,
deviceId: metadata.deviceId ?? target.storedDeviceId
};
}
function loadLegacyBotSdkMetadata(cryptoRootDir) {
const metadataPath = path.join(cryptoRootDir, "bot-sdk.json");
const fallback = { deviceId: null };
const parsed = loadJsonFile(metadataPath);
return { deviceId: typeof parsed?.deviceId === "string" && parsed.deviceId.trim() ? parsed.deviceId : fallback.deviceId };
}
function resolveMatrixLegacyCryptoPlans(params) {
const warnings = [];
const plans = [];
const flatPlan = resolveLegacyMatrixFlatStorePlan(params);
if (flatPlan) if ("warning" in flatPlan) warnings.push(flatPlan.warning);
else plans.push(flatPlan);
for (const accountId of resolveMatrixAccountIds(params.cfg)) {
const target = resolveMatrixMigrationAccountTarget({
cfg: params.cfg,
env: params.env,
accountId
});
if (!target) continue;
const legacyCryptoPath = path.join(target.rootDir, "crypto");
if (!fs.existsSync(legacyCryptoPath)) continue;
const detectedStore = detectLegacyBotSdkCryptoStore(legacyCryptoPath);
if (detectedStore.warning) {
warnings.push(detectedStore.warning);
continue;
}
if (!detectedStore.detected) continue;
if (plans.some((plan) => plan.accountId === accountId && path.resolve(plan.legacyCryptoPath) === path.resolve(legacyCryptoPath))) continue;
const metadata = loadLegacyBotSdkMetadata(legacyCryptoPath);
plans.push({
accountId: target.accountId,
rootDir: target.rootDir,
recoveryKeyPath: path.join(target.rootDir, "recovery-key.json"),
statePath: path.join(target.rootDir, "legacy-crypto-migration.json"),
legacyCryptoPath,
homeserver: target.homeserver,
userId: target.userId,
accessToken: target.accessToken,
deviceId: metadata.deviceId ?? target.storedDeviceId
});
}
return {
plans,
warnings
};
}
function detectLegacyMatrixCrypto(params) {
const detection = resolveMatrixLegacyCryptoPlans({
cfg: params.cfg,
env: params.env ?? process.env
});
const inspectorAvailable = detection.plans.length === 0 || isMatrixLegacyCryptoInspectorAvailable();
if (!inspectorAvailable && detection.plans.length > 0) return {
inspectorAvailable,
plans: detection.plans,
warnings: [...detection.warnings, MATRIX_LEGACY_CRYPTO_INSPECTOR_UNAVAILABLE_MESSAGE]
};
return {
inspectorAvailable,
plans: detection.plans,
warnings: detection.warnings
};
}
async function autoPrepareLegacyMatrixCrypto(params) {
const env = params.env ?? process.env;
const detection = params.deps?.inspectLegacyStore ? resolveMatrixLegacyCryptoPlans({
cfg: params.cfg,
env
}) : detectLegacyMatrixCrypto({
cfg: params.cfg,
env
});
const inspectorAvailable = "inspectorAvailable" in detection ? detection.inspectorAvailable : true;
const warnings = [...detection.warnings];
const changes = [];
if (detection.plans.length === 0) {
if (warnings.length > 0) params.log?.warn?.(`matrix: legacy encrypted-state warnings:\n${warnings.map((entry) => `- ${entry}`).join("\n")}`);
return {
migrated: false,
changes,
warnings
};
}
if (!params.deps?.inspectLegacyStore && !inspectorAvailable) {
if (warnings.length > 0) params.log?.warn?.(`matrix: legacy encrypted-state warnings:\n${warnings.map((entry) => `- ${entry}`).join("\n")}`);
return {
migrated: false,
changes,
warnings
};
}
let inspectLegacyStore = params.deps?.inspectLegacyStore;
if (!inspectLegacyStore) try {
inspectLegacyStore = await loadMatrixLegacyCryptoInspector();
} catch (err) {
const message = formatMatrixErrorMessage(err);
if (!warnings.includes(message)) warnings.push(message);
if (warnings.length > 0) params.log?.warn?.(`matrix: legacy encrypted-state warnings:\n${warnings.map((entry) => `- ${entry}`).join("\n")}`);
return {
migrated: false,
changes,
warnings
};
}
if (!inspectLegacyStore) return {
migrated: false,
changes,
warnings
};
for (const plan of detection.plans) {
try {
migrateLegacyMatrixLegacyCryptoMigrationFileToStore(plan.rootDir);
migrateLegacyMatrixRecoveryKeyFileToStore(plan.rootDir);
} catch (err) {
warnings.push(`Failed migrating Matrix crypto sidecar state for account "${plan.accountId}" (${plan.rootDir}): ${String(err)}`);
}
if (readMatrixLegacyCryptoMigrationState(plan.rootDir)?.version === 1) continue;
if (!plan.deviceId) {
warnings.push(`Legacy Matrix encrypted state detected at ${plan.legacyCryptoPath}, but no device ID was found for account "${plan.accountId}". OpenClaw will continue, but old encrypted history cannot be recovered automatically.`);
continue;
}
let summary;
try {
summary = await inspectLegacyStore({
cryptoRootDir: plan.legacyCryptoPath,
userId: plan.userId,
deviceId: plan.deviceId,
log: params.log?.info
});
} catch (err) {
warnings.push(`Failed inspecting legacy Matrix encrypted state for account "${plan.accountId}" (${plan.legacyCryptoPath}): ${String(err)}`);
continue;
}
let decryptionKeyImported = false;
if (summary.decryptionKeyBase64) {
const existingRecoveryKey = readMatrixRecoveryKeyState(plan.rootDir);
if (existingRecoveryKey?.privateKeyBase64 && existingRecoveryKey.privateKeyBase64 !== summary.decryptionKeyBase64) warnings.push(`Legacy Matrix backup key was found for account "${plan.accountId}", but Matrix SQLite state already contains a different recovery key. Leaving the existing state unchanged.`);
else if (!existingRecoveryKey?.privateKeyBase64) {
const payload = {
version: 1,
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
keyId: null,
privateKeyBase64: summary.decryptionKeyBase64
};
try {
writeMatrixRecoveryKeyState({
storageRootDir: plan.rootDir,
payload
});
changes.push(`Imported Matrix legacy backup key for account "${plan.accountId}" into SQLite`);
decryptionKeyImported = true;
} catch (err) {
warnings.push(`Failed writing Matrix recovery key for account "${plan.accountId}" to SQLite: ${String(err)}`);
}
} else decryptionKeyImported = true;
}
const localOnlyKeys = summary.roomKeyCounts && summary.roomKeyCounts.total > summary.roomKeyCounts.backedUp ? summary.roomKeyCounts.total - summary.roomKeyCounts.backedUp : 0;
if (localOnlyKeys > 0) warnings.push(`Legacy Matrix encrypted state for account "${plan.accountId}" contains ${localOnlyKeys} room key(s) that were never backed up. Backed-up keys can be restored automatically, but local-only encrypted history may remain unavailable after upgrade.`);
if (!summary.decryptionKeyBase64 && (summary.roomKeyCounts?.backedUp ?? 0) > 0) warnings.push(`Legacy Matrix encrypted state for account "${plan.accountId}" has backed-up room keys, but no local backup decryption key was found. Ask the operator to run "openclaw matrix verify backup restore --recovery-key <key>" after upgrade if they have the recovery key.`);
if (!summary.decryptionKeyBase64 && (summary.roomKeyCounts?.total ?? 0) > 0) warnings.push(`Legacy Matrix encrypted state for account "${plan.accountId}" cannot be fully converted automatically because the old rust crypto store does not expose all local room keys for export.`);
if (summary.decryptionKeyBase64 && !decryptionKeyImported && !readMatrixRecoveryKeyState(plan.rootDir)) continue;
const state = {
version: 1,
source: "matrix-bot-sdk-rust",
accountId: plan.accountId,
deviceId: summary.deviceId,
roomKeyCounts: summary.roomKeyCounts,
backupVersion: summary.backupVersion,
decryptionKeyImported,
restoreStatus: decryptionKeyImported ? "pending" : "manual-action-required",
detectedAt: (/* @__PURE__ */ new Date()).toISOString(),
lastError: null
};
try {
writeMatrixLegacyCryptoMigrationState({
storageRootDir: plan.rootDir,
state
});
changes.push(`Prepared Matrix legacy encrypted-state migration for account "${plan.accountId}" in SQLite`);
} catch (err) {
warnings.push(`Failed writing Matrix legacy encrypted-state migration record for account "${plan.accountId}" to SQLite: ${String(err)}`);
}
}
if (changes.length > 0) params.log?.info?.(`matrix: prepared encrypted-state upgrade.\n${changes.map((entry) => `- ${entry}`).join("\n")}`);
if (warnings.length > 0) params.log?.warn?.(`matrix: legacy encrypted-state warnings:\n${warnings.map((entry) => `- ${entry}`).join("\n")}`);
return {
migrated: changes.length > 0,
changes,
warnings
};
}
//#endregion
//#region extensions/matrix/src/migration-snapshot.ts
function resolveMatrixMigrationStatus(params) {
const env = params.env ?? process.env;
const legacyState = detectLegacyMatrixState({
cfg: params.cfg,
env
});
const legacyCrypto = detectLegacyMatrixCrypto({
cfg: params.cfg,
env
});
const actionableLegacyState = legacyState !== null && !("warning" in legacyState);
const actionableLegacyCrypto = legacyCrypto.plans.length > 0 && legacyCrypto.inspectorAvailable;
return {
legacyState,
legacyCrypto,
pending: legacyState !== null || legacyCrypto.plans.length > 0 || legacyCrypto.warnings.length > 0,
actionable: actionableLegacyState || actionableLegacyCrypto
};
}
function hasPendingMatrixMigration(params) {
return resolveMatrixMigrationStatus(params).pending;
}
function hasActionableMatrixMigration(params) {
return resolveMatrixMigrationStatus(params).actionable;
}
//#endregion
export { detectLegacyMatrixCrypto as a, autoPrepareLegacyMatrixCrypto as i, hasPendingMatrixMigration as n, autoMigrateLegacyMatrixState as o, resolveMatrixMigrationStatus as r, detectLegacyMatrixState as s, hasActionableMatrixMigration as t };