UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

317 lines (316 loc) 14.2 kB
import { c as isRecord } from "../../record-coerce-DItp3I4t.js"; import { y as uniqueStrings } from "../../string-normalization-DsCfAx8q.js"; import { o as listAgentIds } from "../../agent-scope-config-DcbEhP0R.js"; import { t as definePluginDoctorMigrationFromPlans } from "../../doctor-migration-plan-adapter-1pCc-VjV.js"; import { o as resolveSessionStorePathCore } from "../../paths-CXdaYWF_.js"; import "../../string-coerce-runtime-GQa0ehRA.js"; import "../../agent-scope-runtime-h57Nypqc.js"; import { n as resolveDefaultTelegramAccountId, t as listTelegramAccountIds } from "../../account-selection-7UzZ8VU5.js"; import "../../session-store-paths-DKZL5csK.js"; import "../../runtime-doctor-migrations-BYo-q8_8.js"; import { t as fileExists } from "../../file-access-runtime-CfcyqU8y.js"; import { n as parseTelegramMessageThreadId } from "../../outbound-params-BUIfhgvx.js"; import { a as listTelegramLegacyBotInfoCacheEntries, n as normalizeCompatibilityConfig, r as TELEGRAM_BOT_INFO_CACHE_NAMESPACE, s as resolveTelegramBotInfoCachePath, t as legacyConfigRules } from "../../doctor-contract-Cq3BM-TT.js"; import { t as resolveTelegramAccountOwnerAgentId } from "../../account-owner-7zwE7Wu9.js"; import { a as resolveTelegramThreadBindingsPath, n as TELEGRAM_THREAD_BINDINGS_NAMESPACE, r as listTelegramLegacyThreadBindingEntries, t as TELEGRAM_THREAD_BINDINGS_MAX_ENTRIES } from "../../thread-bindings-store-BQrk_3J1.js"; import { n as TELEGRAM_STICKER_CACHE_NAMESPACE, r as listTelegramLegacyStickerCacheEntries, t as TELEGRAM_STICKER_CACHE_MAX_ENTRIES } from "../../sticker-cache-store.legacy-state-Bacz6bKV.js"; import { a as normalizeTelegramUpdateOffsetAccountId, i as listTelegramLegacyUpdateOffsetEntries, n as TELEGRAM_UPDATE_OFFSET_NAMESPACE, s as shouldReplaceTelegramUpdateOffsetEntry, t as TELEGRAM_UPDATE_OFFSET_MAX_ENTRIES } from "../../update-offset-store-CeQf7TkD.js"; import { c as TELEGRAM_MESSAGE_CACHE_PERSISTENT_NAMESPACE, d as resolveTelegramMessageCachePath, f as resolveTelegramMessageCachePersistentScopeKey, i as listTelegramLegacySentMessageCacheEntries, l as isTelegramMessageCacheSourceMessage, n as TELEGRAM_SENT_MESSAGE_CACHE_NAMESPACE, s as TELEGRAM_MESSAGE_CACHE_PERSISTENT_MAX_MESSAGES, t as TELEGRAM_SENT_MESSAGE_CACHE_MAX_ENTRIES } from "../../sent-message-cache.legacy-state-RwABm5_I.js"; import { a as resolveTopicNameCachePath, i as resolveTopicNameCacheNamespace, o as resolveTopicNameCacheScope, r as listTelegramLegacyTopicNameCacheEntries, t as TELEGRAM_TOPIC_NAME_CACHE_MAX_ENTRIES } from "../../topic-name-cache-0bursfP6.js"; import fs from "node:fs"; import path from "node:path"; //#region extensions/telegram/src/state-migrations.ts function resolveLegacySessionStorePath(params) { return path.join(resolveMigrationStateDir(params), "sessions", "sessions.json"); } function resolveAgentSessionStorePath(params) { return resolveSessionStorePathCore(params.cfg.session?.store, { env: params.env, agentId: params.agentId }); } function listLegacyAgentSessionStoreSources(params) { const asSource = (targetStorePath) => ({ sourcePath: params.resolveSourcePath(targetStorePath), targetStorePath }); const sources = uniqueStrings([...listAgentIds(params.cfg), "main"].map((agentId) => resolveAgentSessionStorePath({ ...params, agentId }))).map(asSource).filter(({ sourcePath }) => fileExists(sourcePath)); const legacySourcePath = params.resolveSourcePath(resolveLegacySessionStorePath(params)); if (!fileExists(legacySourcePath) || sources.some(({ sourcePath }) => sourcePath === legacySourcePath)) return sources; const targetAgentId = params.preserveLegacyAccountScope && params.cfg.agents?.entries === void 0 && params.cfg.agents?.list === void 0 ? resolveDefaultTelegramAccountId(params.cfg) : resolveTelegramLegacyStateOwnerAgentId(params.cfg); return [...sources, { sourcePath: legacySourcePath, targetStorePath: resolveAgentSessionStorePath({ ...params, agentId: targetAgentId }) }]; } function resolveTelegramLegacyStateOwnerAgentId(cfg) { const configuredAccountIds = listTelegramAccountIds(cfg); const accountIds = configuredAccountIds.length > 0 ? configuredAccountIds : [resolveDefaultTelegramAccountId(cfg)]; const ownerAgentIds = uniqueStrings(accountIds.map((accountId) => resolveTelegramAccountOwnerAgentId({ cfg, accountId }))); if (ownerAgentIds.length === 1) return ownerAgentIds[0]; throw new Error(`Legacy Telegram state has multiple routed owners (${ownerAgentIds.join(", ")}); preserve it until one migration owner is configured.`); } function resolveMigrationStateDir(params) { return params.stateDir ?? path.dirname(path.dirname(path.dirname(path.dirname(resolveSessionStorePathCore(void 0, { env: params.env, agentId: "main" }))))); } function telegramStateImport(params) { return { ...params, kind: "plugin-state-import", targetPath: `plugin state:${params.namespace}`, pluginId: "telegram", scopeKey: params.scopeKey ?? "", cleanupSource: "rename", preview: `- ${params.label}: ${params.sourcePath} → plugin state (${params.namespace})` }; } function parseLegacyMessageCacheJson(text) { try { const value = JSON.parse(text); return Array.isArray(value) ? value : [value]; } catch { return; } } function readLegacyMessageCacheValues(raw) { const text = raw.trim(); const whole = parseLegacyMessageCacheJson(text); if (whole) return whole; const values = []; let jsonl = text; if (text.startsWith("[")) for (const match of text.matchAll(/\](?=\s*\{\s*"key"\s*:)/g)) { const arrayEnd = (match.index ?? -1) + 1; const initial = parseLegacyMessageCacheJson(text.slice(0, arrayEnd)); if (initial) { values.push(...initial); jsonl = text.slice(arrayEnd); break; } } for (const line of jsonl.split("\n")) values.push(...parseLegacyMessageCacheJson(line) ?? []); return values; } function listTelegramLegacyMessageCacheEntries(persistedPath) { let raw; try { raw = fs.readFileSync(persistedPath, "utf8"); } catch { return []; } const entries = /* @__PURE__ */ new Map(); for (const value of readLegacyMessageCacheValues(raw)) { if (!isRecord(value) || typeof value.key !== "string" || !value.key.trim() || !value.key.includes(":") || !isRecord(value.node)) continue; const sourceMessage = value.node.sourceMessage; if (!isTelegramMessageCacheSourceMessage(sourceMessage)) continue; const { openclaw_prompt_context_projection: _projection, ...canonicalSourceMessage } = sourceMessage; const parsedThreadId = parseTelegramMessageThreadId(value.node.threadId); const threadId = parsedThreadId === void 0 ? void 0 : String(parsedThreadId); const key = `${value.key.slice(0, value.key.lastIndexOf(":") + 1)}${sourceMessage.message_id}`; entries.delete(key); entries.set(key, { version: 1, sourceMessage: canonicalSourceMessage, ...threadId ? { threadId } : {} }); if (entries.size > 3e3) { const oldest = entries.keys().next().value; if (oldest !== void 0) entries.delete(oldest); } } return Array.from(entries, ([key, value]) => ({ key, value })); } function listTelegramLegacySidecarAccountIds(params) { let persistedAccountIds; try { persistedAccountIds = fs.readdirSync(path.join(params.stateDir, "telegram"), { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.startsWith(params.prefix) && entry.name.endsWith(params.suffix)).map((entry) => entry.name.slice(params.prefix.length, -params.suffix.length)).filter(Boolean); } catch { persistedAccountIds = []; } return uniqueStrings([...listTelegramAccountIds(params.cfg), ...persistedAccountIds]); } function detectTelegramMessageCacheLegacyStateMigration(params) { return listLegacyAgentSessionStoreSources({ ...params, resolveSourcePath: resolveTelegramMessageCachePath }).map(({ sourcePath, targetStorePath }) => telegramStateImport({ label: "Telegram prompt-context message cache", sourcePath, namespace: TELEGRAM_MESSAGE_CACHE_PERSISTENT_NAMESPACE, maxEntries: TELEGRAM_MESSAGE_CACHE_PERSISTENT_MAX_MESSAGES, scopeKey: resolveTelegramMessageCachePersistentScopeKey(resolveTelegramMessageCachePath(targetStorePath)), readEntries: () => listTelegramLegacyMessageCacheEntries(sourcePath) })); } function detectTelegramBotInfoCacheLegacyStateMigration(params) { return listTelegramAccountIds(params.cfg).flatMap((accountId) => { const persistedPath = resolveTelegramBotInfoCachePath(accountId, params.env); if (!fileExists(persistedPath)) return []; return telegramStateImport({ label: "Telegram startup bot info cache", sourcePath: persistedPath, namespace: TELEGRAM_BOT_INFO_CACHE_NAMESPACE, maxEntries: 128, readEntries: () => { return listTelegramLegacyBotInfoCacheEntries({ accountId, persistedPath }); } }); }); } async function detectTelegramUpdateOffsetLegacyStateMigration(params) { const { resolveTelegramToken } = await import("../../telegram/token.js"); const stateDir = resolveMigrationStateDir(params); return listTelegramLegacySidecarAccountIds({ cfg: params.cfg, stateDir, prefix: "update-offset-", suffix: ".json" }).flatMap((accountId) => { const normalized = normalizeTelegramUpdateOffsetAccountId(accountId); const persistedPath = path.join(stateDir, "telegram", `update-offset-${normalized}.json`); if (!fileExists(persistedPath)) return []; let botToken; try { botToken = resolveTelegramToken(params.cfg, { accountId, envToken: params.env.TELEGRAM_BOT_TOKEN }).token || void 0; } catch { botToken = void 0; } return telegramStateImport({ label: "Telegram update offset", sourcePath: persistedPath, namespace: TELEGRAM_UPDATE_OFFSET_NAMESPACE, maxEntries: TELEGRAM_UPDATE_OFFSET_MAX_ENTRIES, readEntries: () => listTelegramLegacyUpdateOffsetEntries({ accountId, persistedPath }), shouldReplaceExistingEntry: ({ existingValue, incomingValue }) => shouldReplaceTelegramUpdateOffsetEntry({ existingValue, incomingValue, botToken }) }); }); } function detectTelegramStickerCacheLegacyStateMigration(params) { const stateDir = resolveMigrationStateDir(params); const persistedPath = path.join(stateDir, "telegram", "sticker-cache.json"); if (!fileExists(persistedPath)) return []; return [telegramStateImport({ label: "Telegram sticker cache", sourcePath: persistedPath, namespace: TELEGRAM_STICKER_CACHE_NAMESPACE, maxEntries: TELEGRAM_STICKER_CACHE_MAX_ENTRIES, readEntries: () => listTelegramLegacyStickerCacheEntries({ persistedPath }) })]; } function detectTelegramSentMessageCacheLegacyStateMigration(params) { return listLegacyAgentSessionStoreSources({ ...params, resolveSourcePath: (storePath) => `${storePath}.telegram-sent-messages.json` }).map(({ sourcePath, targetStorePath }) => telegramStateImport({ label: "Telegram sent-message cache", sourcePath, namespace: TELEGRAM_SENT_MESSAGE_CACHE_NAMESPACE, maxEntries: TELEGRAM_SENT_MESSAGE_CACHE_MAX_ENTRIES, cleanupWhenEmpty: true, readEntries: () => listTelegramLegacySentMessageCacheEntries({ persistedPath: sourcePath, targetStorePath }) })); } function detectTelegramThreadBindingLegacyStateMigration(params) { const stateDir = resolveMigrationStateDir(params); return listTelegramLegacySidecarAccountIds({ cfg: params.cfg, stateDir, prefix: "thread-bindings-", suffix: ".json" }).flatMap((accountId) => { const persistedPath = resolveTelegramThreadBindingsPath(accountId, params.env); if (!fileExists(persistedPath)) return []; return telegramStateImport({ label: "Telegram thread bindings", sourcePath: persistedPath, namespace: TELEGRAM_THREAD_BINDINGS_NAMESPACE, maxEntries: TELEGRAM_THREAD_BINDINGS_MAX_ENTRIES, readEntries: () => listTelegramLegacyThreadBindingEntries({ accountId, persistedPath }) }); }); } function topicNameCacheImportSource(sourcePath, targetStorePath) { return { sourcePath, namespace: resolveTopicNameCacheNamespace(resolveTopicNameCacheScope(targetStorePath)) }; } function detectTelegramTopicNameCacheLegacyStateMigration(params) { const accountSources = listTelegramAccountIds(params.cfg).map((accountId) => { const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { env: params.env, agentId: accountId }); return topicNameCacheImportSource(resolveTopicNameCachePath(storePath), storePath); }); const sessionSources = listLegacyAgentSessionStoreSources({ ...params, resolveSourcePath: resolveTopicNameCachePath, preserveLegacyAccountScope: true }).map(({ sourcePath, targetStorePath }) => topicNameCacheImportSource(sourcePath, targetStorePath)); return [...new Map([...accountSources.filter((source) => fileExists(source.sourcePath)), ...sessionSources].map((source) => [`${source.sourcePath}\0${source.namespace}`, source])).values()].map((source) => telegramStateImport({ label: "Telegram forum topic-name cache", sourcePath: source.sourcePath, namespace: source.namespace, maxEntries: TELEGRAM_TOPIC_NAME_CACHE_MAX_ENTRIES, readEntries: () => { return listTelegramLegacyTopicNameCacheEntries({ persistedPath: source.sourcePath, maxEntries: TELEGRAM_TOPIC_NAME_CACHE_MAX_ENTRIES }); } })); } async function detectTelegramLegacyStateMigrations(params) { const plans = []; plans.push(...await detectTelegramUpdateOffsetLegacyStateMigration(params)); plans.push(...detectTelegramBotInfoCacheLegacyStateMigration(params)); plans.push(...detectTelegramStickerCacheLegacyStateMigration(params)); plans.push(...detectTelegramMessageCacheLegacyStateMigration(params)); plans.push(...detectTelegramSentMessageCacheLegacyStateMigration(params)); plans.push(...detectTelegramTopicNameCacheLegacyStateMigration(params)); plans.push(...detectTelegramThreadBindingLegacyStateMigration(params)); return plans; } //#endregion //#region extensions/telegram/doctor-contract-api.ts const stateMigrations = [definePluginDoctorMigrationFromPlans({ id: "telegram-legacy-state", label: "Telegram legacy state", resolvePlans: detectTelegramLegacyStateMigrations })]; //#endregion export { legacyConfigRules, normalizeCompatibilityConfig, stateMigrations };