UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

235 lines (234 loc) 9 kB
import { P as timestampMsToIsoString } from "./number-coercion-CJQ8TR--.js"; import { n as readJsonFileWithFallback } from "./json-store-CWaMsrLM.js"; import "./number-runtime-DBLVDypr.js"; import { t as getMatrixRuntime } from "./runtime-CN4Os2vf.js"; import { t as formatMatrixErrorMessage } from "./errors-C4iaVh6O.js"; import { O as resolveMatrixSqliteStateEnv } from "./crypto-state-store-Dh3bqPTW.js"; import { o as resolveMatrixStoragePaths, r as recordCurrentStorageMetaDeviceId } from "./storage-6UUh27xf.js"; import path from "node:path"; import fs from "node:fs/promises"; import { createHash } from "node:crypto"; //#region extensions/matrix/src/matrix/monitor/startup-verification.ts const STARTUP_VERIFICATION_STATE_FILENAME = "startup-verification.json"; const STARTUP_VERIFICATION_NAMESPACE = "startup-verification"; const STARTUP_VERIFICATION_MIGRATIONS_NAMESPACE = "startup-verification-migrations"; const STARTUP_VERIFICATION_MAX_ENTRIES = 1e3; const DEFAULT_STARTUP_VERIFICATION_MODE = "if-unverified"; const DEFAULT_STARTUP_VERIFICATION_COOLDOWN_HOURS = 24; const DEFAULT_STARTUP_VERIFICATION_FAILURE_COOLDOWN_MS = 3600 * 1e3; function normalizeCooldownHours(value) { if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_STARTUP_VERIFICATION_COOLDOWN_HOURS; return Math.max(0, value); } function resolveStartupVerificationStatePath(params) { const storagePaths = resolveMatrixStoragePaths({ homeserver: params.auth.homeserver, userId: params.auth.userId, accessToken: params.auth.accessToken, accountId: params.auth.accountId, deviceId: params.auth.deviceId, env: params.env, stateDir: params.stateDir }); return path.join(storagePaths.rootDir, STARTUP_VERIFICATION_STATE_FILENAME); } function buildStartupVerificationKey(auth) { return auth.accountId.trim() || "default"; } function createStartupVerificationStore(params) { return getMatrixRuntime().state.openKeyedStore({ namespace: STARTUP_VERIFICATION_NAMESPACE, maxEntries: STARTUP_VERIFICATION_MAX_ENTRIES, env: resolveMatrixSqliteStateEnv(params) }); } function createStartupVerificationMigrationStore(params) { return getMatrixRuntime().state.openKeyedStore({ namespace: STARTUP_VERIFICATION_MIGRATIONS_NAMESPACE, maxEntries: STARTUP_VERIFICATION_MAX_ENTRIES, env: resolveMatrixSqliteStateEnv(params) }); } function buildStartupVerificationImportKey(params) { const accountId = params.auth.accountId.trim() || "default"; return `${accountId}:${createHash("sha256").update(accountId).update("\0").update(params.legacyFilePath).digest("hex")}`; } async function readLegacyStartupVerificationState(filePath) { const { value } = await readJsonFileWithFallback(filePath, null); return value && typeof value === "object" ? value : null; } async function readStartupVerificationState(params) { const store = createStartupVerificationStore(params); const key = buildStartupVerificationKey(params.auth); const value = await store.lookup(key); if (value && typeof value === "object") return value; const migrationStore = createStartupVerificationMigrationStore(params); const legacyImportKey = buildStartupVerificationImportKey({ auth: params.auth, legacyFilePath: params.legacyFilePath }); if (await migrationStore.lookup(legacyImportKey)) return null; const legacy = await readLegacyStartupVerificationState(params.legacyFilePath); if (legacy) await store.register(key, legacy).then(async () => { if (typeof legacy.deviceId === "string" && legacy.deviceId.trim()) recordCurrentStorageMetaDeviceId({ rootDir: path.dirname(params.legacyFilePath), deviceId: legacy.deviceId }); await migrationStore.register(legacyImportKey, { importedAt: Date.now() }); await fs.rm(params.legacyFilePath, { force: true }).catch(() => {}); }).catch(() => {}); return legacy; } async function writeStartupVerificationState(params) { await createStartupVerificationStore(params).register(buildStartupVerificationKey(params.auth), params.state); await createStartupVerificationMigrationStore(params).register(buildStartupVerificationImportKey({ auth: params.auth, legacyFilePath: params.legacyFilePath }), { importedAt: Date.now() }).catch(() => {}); if (typeof params.state.deviceId === "string" && params.state.deviceId.trim()) recordCurrentStorageMetaDeviceId({ rootDir: path.dirname(params.legacyFilePath), deviceId: params.state.deviceId }); await fs.rm(params.legacyFilePath, { force: true }).catch(() => {}); } async function clearStartupVerificationState(params) { await createStartupVerificationStore(params).delete(buildStartupVerificationKey(params.auth)).catch(() => {}); await createStartupVerificationMigrationStore(params).register(buildStartupVerificationImportKey({ auth: params.auth, legacyFilePath: params.legacyFilePath }), { importedAt: Date.now() }).catch(() => {}); await fs.rm(params.legacyFilePath, { force: true }).catch(() => {}); } function resolveStateCooldownMs(state, cooldownMs) { if (state?.outcome === "failed") return Math.min(cooldownMs, DEFAULT_STARTUP_VERIFICATION_FAILURE_COOLDOWN_MS); return cooldownMs; } function resolveRetryAfterMs(params) { const attemptedAtMs = Date.parse(params.attemptedAt ?? ""); if (!Number.isFinite(attemptedAtMs)) return; const remaining = attemptedAtMs + params.cooldownMs - params.nowMs; return remaining > 0 ? remaining : void 0; } function resolveStartupVerificationTimestamp(nowMs) { return timestampMsToIsoString(nowMs) ?? timestampMsToIsoString(Date.now()) ?? "1970-01-01T00:00:00.000Z"; } function shouldHonorCooldown(params) { if (!params.state || params.stateCooldownMs <= 0) return false; if (params.state.userId && params.verification.userId && params.state.userId !== params.verification.userId) return false; if (params.state.deviceId && params.verification.deviceId && params.state.deviceId !== params.verification.deviceId) return false; return resolveRetryAfterMs({ attemptedAt: params.state.attemptedAt, cooldownMs: params.stateCooldownMs, nowMs: params.nowMs }) !== void 0; } function hasPendingSelfVerification(verifications) { return verifications.some((entry) => entry.isSelfVerification && !entry.completed && entry.pending); } async function ensureMatrixStartupVerification(params) { if (params.auth.encryption !== true || !params.client.crypto) return { kind: "unsupported" }; const verification = await params.client.getOwnDeviceVerificationStatus(); const statePath = params.stateFilePath ?? resolveStartupVerificationStatePath({ auth: params.auth, env: params.env, stateDir: params.stateDir }); const stateDir = params.stateDir ?? path.dirname(statePath); if (verification.verified) { await clearStartupVerificationState({ auth: params.auth, env: params.env, stateDir, legacyFilePath: statePath }); return { kind: "verified", verification }; } if ((params.accountConfig.startupVerification ?? DEFAULT_STARTUP_VERIFICATION_MODE) === "off") { await clearStartupVerificationState({ auth: params.auth, env: params.env, stateDir, legacyFilePath: statePath }); return { kind: "disabled", verification }; } if (hasPendingSelfVerification(await params.client.crypto.listVerifications().catch(() => []))) return { kind: "pending", verification }; const cooldownMs = normalizeCooldownHours(params.accountConfig.startupVerificationCooldownHours) * 60 * 60 * 1e3; const nowMs = params.nowMs ?? Date.now(); const attemptedAt = resolveStartupVerificationTimestamp(nowMs); const state = await readStartupVerificationState({ auth: params.auth, env: params.env, stateDir, legacyFilePath: statePath }); const stateCooldownMs = resolveStateCooldownMs(state, cooldownMs); if (shouldHonorCooldown({ state, verification, stateCooldownMs, nowMs })) return { kind: "cooldown", verification, retryAfterMs: resolveRetryAfterMs({ attemptedAt: state?.attemptedAt, cooldownMs: stateCooldownMs, nowMs }) }; try { const request = await params.client.crypto.requestVerification({ ownUser: true }); await writeStartupVerificationState({ auth: params.auth, env: params.env, stateDir, legacyFilePath: statePath, state: { userId: verification.userId, deviceId: verification.deviceId, attemptedAt, outcome: "requested", requestId: request.id, transactionId: request.transactionId } }); return { kind: "requested", verification, requestId: request.id, transactionId: request.transactionId ?? void 0 }; } catch (err) { const error = formatMatrixErrorMessage(err); await writeStartupVerificationState({ auth: params.auth, env: params.env, stateDir, legacyFilePath: statePath, state: { userId: verification.userId, deviceId: verification.deviceId, attemptedAt, outcome: "failed", error } }).catch(() => {}); return { kind: "request-failed", verification, error }; } } //#endregion export { ensureMatrixStartupVerification };