UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

174 lines (173 loc) 8.95 kB
import { t as formatCliCommand } from "./command-format-C7YfyMTd.js"; import { m as resolveAgentWorkspaceDir } from "./agent-scope-config-DcbEhP0R.js"; import { w as resolveStateDir } from "./paths-D2sRr1a_.js"; import { f as resolveAgentIdFromSessionKey } from "./session-key-BnWWjqNc.js"; import { r as executeSqliteQuerySync } from "./kysely-sync-COmh4HWh.js"; import "./agent-scope-DbtJyKUL.js"; import { k as resolveOpenClawAgentSqlitePath } from "./openclaw-agent-db-lease-Djvd6LWN.js"; import { y as listOpenClawRegisteredAgentDatabases } from "./openclaw-agent-db-maintenance-wTIy-jt-.js"; import { b as closeOpenClawAgentDatabaseByPath, p as openOpenClawAgentDatabase, u as isOpenClawAgentDatabaseOpen } from "./openclaw-agent-db-CWtDoRbC.js"; import "./session-accessor-YsytfDtG.js"; import { n as withOpenClawAgentDatabaseReadOnly } from "./openclaw-agent-db-readonly-CHjf8FxN.js"; import { d as patchSessionEntryCore } from "./session-accessor.sqlite-entry-CWk3jL7s.js"; import { o as parseReadableSqliteSessionEntryRow } from "./session-accessor.sqlite-entry-store-BxYl0nro.js"; import { c as resolveSqliteScope, i as getSessionKysely, m as toDatabaseOptions, s as resolveSqliteReadScope } from "./session-accessor.sqlite-scope-2KfMzb44.js"; import { _ as setCanonicalSqliteSessionMainKey, h as isCanonicalSqliteSessionMainKeyCurrent } from "./session-accessor.sqlite-transcript-state-DwF2owZS.js"; import { o as resolveAllAgentSessionStoreTargetsSync } from "./targets-Cknmo7YZ.js"; import { b as listRegistryWorktreesForMigration } from "./registry-Dvi_fTvk.js"; import { t as migrateLegacyMainSessionKeys } from "./legacy-main-session-migration-BUkRsA4g.js"; import { l as resolveProjectRegistry } from "./project-registry-Cm-dqnhv.js"; import fs from "node:fs"; import path from "node:path"; import { sql } from "kysely"; //#region src/config/sessions/worktree-workspace-migration.ts function isInside(root, target) { const relative = path.relative(root, target); return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative); } function resolveLegacyCanonicalWorkspace(params) { const worktree = params.entry.worktree; if (!worktree || worktree.canonicalWorkspaceDir) return; const recordedRepoRoot = path.resolve(worktree.repoRoot); if (params.entry.projectId) { const project = resolveProjectRegistry(params.cfg, params.entry.projectId, { env: params.env }); const projectRoot = project ? path.resolve(project.repoRoot) : void 0; return projectRoot && isInside(recordedRepoRoot, projectRoot) ? projectRoot : void 0; } const record = params.worktrees.find((candidate) => candidate.id === worktree.id); const spawnedCwd = params.entry.spawnedCwd; if (record?.ownerKind === "session" && record.ownerId === params.sessionKey && spawnedCwd && path.resolve(record.repoRoot) === path.resolve(worktree.repoRoot)) { const relative = path.relative(path.resolve(record.path), path.resolve(spawnedCwd)); if (relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative)) return path.resolve(recordedRepoRoot, relative); } const agentWorkspace = path.resolve(resolveAgentWorkspaceDir(params.cfg, params.agentId, params.env)); return agentWorkspace && agentWorkspace === recordedRepoRoot ? agentWorkspace : void 0; } function listLegacyWorktreeSessionEntries(params) { const resolved = resolveSqliteScope({ ...params, sessionKey: "" }); const result = withOpenClawAgentDatabaseReadOnly((database) => { const db = getSessionKysely(database.db); return executeSqliteQuerySync(database.db, db.selectFrom("session_nodes").selectAll().where(sql`session_nodes.entry_valid != 1 OR ( json_valid(session_nodes.entry_json) AND json_type(session_nodes.entry_json, '$.worktree') = 'object' AND ( json_type(session_nodes.entry_json, '$.worktree.canonicalWorkspaceDir') IS NULL OR json_extract(session_nodes.entry_json, '$.worktree.canonicalWorkspaceDir') = '' ) )`).orderBy("session_key", "asc")).rows.flatMap((row) => { const entry = parseReadableSqliteSessionEntryRow(database, row); return entry ? [{ databasePath: database.path, entry, sessionKey: row.session_key }] : []; }); }, toDatabaseOptions(resolved)); return result.found ? result.value : []; } async function migrateManagedWorktreeCanonicalWorkspaces(params) { const env = params.env ?? process.env; const worktrees = listRegistryWorktreesForMigration(env); let migrated = 0; for (const { databasePath, entry, sessionKey } of listLegacyWorktreeSessionEntries({ agentId: params.agentId, env, storePath: params.storePath })) { const agentId = resolveAgentIdFromSessionKey(sessionKey, params.agentId); const canonicalWorkspaceDir = resolveLegacyCanonicalWorkspace({ agentId, cfg: params.cfg, entry, env, sessionKey, worktrees }); if (!canonicalWorkspaceDir) continue; if ((await patchSessionEntryCore({ agentId, env, sessionKey, storePath: databasePath }, (current) => { if (!current.worktree || current.worktree.id !== entry.worktree?.id || current.worktree.canonicalWorkspaceDir) return null; return { worktree: { ...current.worktree, canonicalWorkspaceDir } }; }, { preserveActivity: true, skipMaintenance: true }))?.worktree?.canonicalWorkspaceDir === canonicalWorkspaceDir) migrated += 1; } return migrated; } //#endregion //#region src/config/sessions/startup-migration.ts function assertSessionStoreMigrationComplete(params) { const env = params.env ?? process.env; const targets = params.targets ?? resolveAllAgentSessionStoreTargetsSync(params.cfg, { env }); const legacyStore = [path.join(resolveStateDir(env), "sessions", "sessions.json"), ...targets.map((target) => target.storePath)].find((storePath) => !storePath.endsWith(".sqlite") && fs.existsSync(storePath)); if (legacyStore) throw new Error(`Legacy session store requires migration: ${legacyStore}. Run "${formatCliCommand("openclaw doctor --fix", env)}" against the same state/config before starting OpenClaw.`); } /** Maintains existing stores, optionally handing each live database to its runtime owner. */ async function runSessionStartupMigration(params) { const env = params.env ?? process.env; const resolveTargets = params.deps?.resolveAllAgentSessionStoreTargetsSync ?? resolveAllAgentSessionStoreTargetsSync; let targets = resolveTargets(params.cfg, { env }); assertSessionStoreMigrationComplete({ cfg: params.cfg, env, targets }); const result = await (params.deps?.migrateLegacyMainSessionKeys ?? migrateLegacyMainSessionKeys)({ cfg: params.cfg, env, mode: "automatic" }); if (result.changes.length > 0) params.log.info(`session: migrated retired main-agent session keys:\n${result.changes.map((change) => `- ${change}`).join("\n")}`); if (result.warnings.length > 0) params.log.warn(`session: retired main-agent session migration warnings:\n${result.warnings.map((warning) => `- ${warning}`).join("\n")}`); if (result.armed) targets = resolveTargets(params.cfg, { env }); const databases = /* @__PURE__ */ new Set(); const migrateWorktreeSessions = params.deps?.migrateManagedWorktreeCanonicalWorkspaces ?? migrateManagedWorktreeCanonicalWorkspaces; const registeredDatabases = new Set(listOpenClawRegisteredAgentDatabases({ env }).map((entry) => `${entry.agentId}\0${entry.path}`)); let migratedWorktreeSessions = 0; for (const target of targets) { const options = toDatabaseOptions(resolveSqliteReadScope({ ...target, env })); const databasePath = resolveOpenClawAgentSqlitePath(options); if (databases.has(databasePath) || !fs.existsSync(databasePath)) continue; databases.add(databasePath); const alreadyOpen = isOpenClawAgentDatabaseOpen(databasePath); let handedOff = false; try { try { if (!registeredDatabases.has(`${options.agentId}\0${databasePath}`) || !isCanonicalSqliteSessionMainKeyCurrent(options, params.cfg.session?.mainKey)) { const database = openOpenClawAgentDatabase(options); setCanonicalSqliteSessionMainKey(database, params.cfg.session?.mainKey); } if (!result.armed || result.complete) migratedWorktreeSessions += await migrateWorktreeSessions({ ...target, cfg: params.cfg, env }); } catch (error) { params.log.warn(`session: SQLite startup maintenance failed for ${target.agentId}; continuing: ${String(error)}`); } if (params.handoffDatabase) { await params.handoffDatabase(options); handedOff = true; } } finally { if (!alreadyOpen && !handedOff && isOpenClawAgentDatabaseOpen(databasePath)) closeOpenClawAgentDatabaseByPath(databasePath); } } if (migratedWorktreeSessions > 0) params.log.info(`session: recorded canonical workspaces for ${migratedWorktreeSessions} managed-worktree session(s)`); } //#endregion export { runSessionStartupMigration as n, assertSessionStoreMigrationComplete as t };