UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

152 lines (151 loc) 7.42 kB
import { n as resolvePreferredOpenClawTmpDir } from "./tmp-openclaw-dir-DnyL0lW9.js"; import { t as openNodeSqliteDatabase } from "./node-sqlite-BpQX3W0e.js"; import { r as withTempWorkspace } from "./private-temp-workspace-Dh7teFP0.js"; import "./sqlite-runtime-BpLlraG0.js"; import "./temp-path-DcDoMvSD.js"; import { i as applyMigrationManualItem, l as markMigrationItemConflict, r as applyMigrationConfigPatchItem, u as markMigrationItemError, y as summarizeMigrationItems } from "./migration-q6QzXKht.js"; import { a as resolvePlannedMigrationTargets, i as copyMigrationFileItem, o as withCachedMigrationConfigRuntime, r as copyMemoryMigrationFileItem, s as writeMigrationReport, t as archiveMigrationItem } from "./migration-runtime-C428wS8a.js"; import { t as appendItem } from "./helpers-DWNr52wp.js"; import { c as HERMES_REASON_MODEL_PROVIDER_CONFLICT, f as findHermesModelProviderDependency, g as readHermesModelDetails } from "./items-BfpzP26U.js"; import { t as applyAuthItem } from "./auth-BAfkel6H.js"; import { t as applyModelItem } from "./model-jtqSnRdw.js"; import "./targets-Tlfsh6sF.js"; import { t as applySecretItem } from "./secrets-BUC8QB8Q.js"; import { t as buildHermesPlan } from "./plan-DoFBZzGL.js"; import path from "node:path"; import fs from "node:fs/promises"; //#region extensions/migrate-hermes/apply.ts const HERMES_SQLITE_SNAPSHOT_PREFIX = "openclaw-migrate-hermes-sqlite-"; function isHermesMemoryOnlyCopyItem(item) { return item.kind === "memory" && item.action === "copy" && item.details?.sourceType === "hermes-memory" && item.details?.collectionId === "hermes"; } function assertConsistentMemoryPlan(plan) { const hasMemoryOnlyCopy = plan.items.some(isHermesMemoryOnlyCopyItem); const hasMemoryAppend = plan.items.some((item) => item.kind === "memory" && item.action === "append"); if (hasMemoryOnlyCopy && hasMemoryAppend) throw new Error("Hermes migration plan mixes memory-only copy and append items"); } async function archiveHermesItem(item, reportDir) { if (!item.source || path.extname(item.source) !== ".db") return await archiveMigrationItem(item, reportDir); const sourcePath = item.source; let sourceStat; try { sourceStat = await fs.lstat(sourcePath); } catch { return await archiveMigrationItem(item, reportDir); } if (!sourceStat.isFile()) return await archiveMigrationItem(item, reportDir); try { return await withTempWorkspace({ rootDir: resolvePreferredOpenClawTmpDir(), prefix: HERMES_SQLITE_SNAPSHOT_PREFIX }, async ({ dir: tempDir }) => { const snapshotPath = path.join(tempDir, path.basename(sourcePath)); const source = openNodeSqliteDatabase(sourcePath, { readOnly: true }); try { source.exec("PRAGMA busy_timeout = 30000;"); source.prepare("VACUUM INTO ?").run(snapshotPath); } finally { source.close(); } await fs.chmod(snapshotPath, 384); return { ...await archiveMigrationItem({ ...item, source: snapshotPath }, reportDir), source: sourcePath }; }); } catch (err) { const snapshotReason = err instanceof Error ? err.message : String(err); let recoveryArchive; try { recoveryArchive = await withTempWorkspace({ rootDir: resolvePreferredOpenClawTmpDir(), prefix: HERMES_SQLITE_SNAPSHOT_PREFIX }, async ({ dir: tempDir }) => { const recoveryDir = path.join(tempDir, `${path.basename(sourcePath)}-recovery`); await fs.mkdir(recoveryDir, { recursive: true }); for (const candidate of [ sourcePath, `${sourcePath}-wal`, `${sourcePath}-shm` ]) { if (!(await fs.lstat(candidate).catch(() => void 0))?.isFile()) continue; try { await fs.copyFile(candidate, path.join(recoveryDir, path.basename(candidate))); } catch (copyError) { const copyCode = copyError.code; if (candidate !== sourcePath && copyCode === "ENOENT") continue; throw copyError; } } return await archiveMigrationItem({ ...item, source: recoveryDir }, reportDir); }); } catch (recoveryError) { const recoveryReason = recoveryError instanceof Error ? recoveryError.message : String(recoveryError); return markMigrationItemError(item, `SQLite snapshot failed: ${snapshotReason}; recovery archive failed: ${recoveryReason}`); } if (recoveryArchive.status === "migrated") return markMigrationItemError({ ...recoveryArchive, source: sourcePath }, `SQLite snapshot failed; database recovery files preserved for manual review: ${snapshotReason}`); return markMigrationItemError({ ...recoveryArchive, source: sourcePath }, `SQLite snapshot failed: ${snapshotReason}; recovery archive failed: ${recoveryArchive.reason ?? recoveryArchive.status}`); } } async function applyHermesPlan(params) { const plan = params.plan ?? await buildHermesPlan(params.ctx); assertConsistentMemoryPlan(plan); const reportDir = params.ctx.reportDir ?? path.join(params.ctx.stateDir, "migration", "hermes"); const targets = resolvePlannedMigrationTargets(params.ctx); const appliedByItem = /* @__PURE__ */ new Map(); const runtime = withCachedMigrationConfigRuntime(params.ctx.runtime ?? params.runtime, params.ctx.config); const applyCtx = { ...params.ctx, runtime }; const executionItems = [...plan.items.filter((item) => item.id.startsWith("config:model-provider:")), ...plan.items.filter((item) => !item.id.startsWith("config:model-provider:"))]; for (const item of executionItems) { if (item.status !== "planned") { appliedByItem.set(item, item); continue; } let appliedItem; if (item.id === "config:default-model") { const model = readHermesModelDetails(item)?.model; const dependency = model ? findHermesModelProviderDependency(plan.items, model) : void 0; const dependencyResult = dependency ? appliedByItem.get(dependency) : void 0; if (dependencyResult && dependencyResult.status !== "migrated") appliedItem = dependencyResult.status === "conflict" ? markMigrationItemConflict(item, HERMES_REASON_MODEL_PROVIDER_CONFLICT) : markMigrationItemError(item, `model provider config failed: ${dependencyResult.reason ?? dependencyResult.status}`); else appliedItem = await applyModelItem(applyCtx, item); } else if (item.kind === "config") appliedItem = await applyMigrationConfigPatchItem(applyCtx, item); else if (item.kind === "manual") appliedItem = applyMigrationManualItem(item); else if (item.action === "archive") appliedItem = await archiveHermesItem(item, reportDir); else if (item.kind === "auth") appliedItem = await applyAuthItem(applyCtx, item, targets); else if (item.kind === "secret") appliedItem = await applySecretItem(applyCtx, item, targets); else if (isHermesMemoryOnlyCopyItem(item)) appliedItem = await copyMemoryMigrationFileItem(item, reportDir, { workspaceDir: targets.workspaceDir, overwrite: params.ctx.overwrite }); else if (item.action === "append") appliedItem = await appendItem(item); else appliedItem = await copyMigrationFileItem(item, reportDir, { overwrite: params.ctx.overwrite }); appliedByItem.set(item, appliedItem); } const items = plan.items.map((item) => appliedByItem.get(item) ?? item); const result = { ...plan, items, summary: summarizeMigrationItems(items), backupPath: params.ctx.backupPath, reportDir }; await writeMigrationReport(result, { title: "Hermes Migration Report" }); return result; } //#endregion export { applyHermesPlan as t };