openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
174 lines (173 loc) • 8.95 kB
JavaScript
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 };