import type { IncomingMessage, ServerResponse } from 'http'; import { join } from 'node:path'; import { getOpenClawConfigDir } from '../../utils/paths'; import type { HostApiContext } from '../context'; import { parseJsonBody, sendJson } from '../route-utils'; export async function handleSessionRoutes( req: IncomingMessage, res: ServerResponse, url: URL, _ctx: HostApiContext, ): Promise { if (url.pathname === '/api/sessions/delete' && req.method === 'POST') { try { const body = await parseJsonBody<{ sessionKey: string }>(req); const sessionKey = body.sessionKey; if (!sessionKey || !sessionKey.startsWith('agent:')) { sendJson(res, 400, { success: false, error: `Invalid sessionKey: ${sessionKey}` }); return true; } const parts = sessionKey.split(':'); if (parts.length < 3) { sendJson(res, 400, { success: false, error: `sessionKey has too few parts: ${sessionKey}` }); return true; } const agentId = parts[1]; const sessionsDir = join(getOpenClawConfigDir(), 'agents', agentId, 'sessions'); const sessionsJsonPath = join(sessionsDir, 'sessions.json'); const fsP = await import('node:fs/promises'); const raw = await fsP.readFile(sessionsJsonPath, 'utf8'); const sessionsJson = JSON.parse(raw) as Record; let uuidFileName: string | undefined; let resolvedSrcPath: string | undefined; if (Array.isArray(sessionsJson.sessions)) { const entry = (sessionsJson.sessions as Array>) .find((s) => s.key === sessionKey || s.sessionKey === sessionKey); if (entry) { uuidFileName = (entry.file ?? entry.fileName ?? entry.path) as string | undefined; if (!uuidFileName && typeof entry.id === 'string') { uuidFileName = `${entry.id}.jsonl`; } } } if (!uuidFileName && sessionsJson[sessionKey] != null) { const val = sessionsJson[sessionKey]; if (typeof val === 'string') { uuidFileName = val; } else if (typeof val === 'object' && val !== null) { const entry = val as Record; const absFile = (entry.sessionFile ?? entry.file ?? entry.fileName ?? entry.path) as string | undefined; if (absFile) { if (absFile.startsWith('/') || absFile.match(/^[A-Za-z]:\\/)) { resolvedSrcPath = absFile; } else { uuidFileName = absFile; } } else { const uuidVal = (entry.id ?? entry.sessionId) as string | undefined; if (uuidVal) uuidFileName = uuidVal.endsWith('.jsonl') ? uuidVal : `${uuidVal}.jsonl`; } } } if (!uuidFileName && !resolvedSrcPath) { sendJson(res, 404, { success: false, error: `Cannot resolve file for session: ${sessionKey}` }); return true; } if (!resolvedSrcPath) { if (!uuidFileName!.endsWith('.jsonl')) uuidFileName = `${uuidFileName}.jsonl`; resolvedSrcPath = join(sessionsDir, uuidFileName!); } const dstPath = resolvedSrcPath.replace(/\.jsonl$/, '.deleted.jsonl'); try { await fsP.access(resolvedSrcPath); await fsP.rename(resolvedSrcPath, dstPath); } catch { // Non-fatal; still try to update sessions.json. } const raw2 = await fsP.readFile(sessionsJsonPath, 'utf8'); const json2 = JSON.parse(raw2) as Record; if (Array.isArray(json2.sessions)) { json2.sessions = (json2.sessions as Array>) .filter((s) => s.key !== sessionKey && s.sessionKey !== sessionKey); } else if (json2[sessionKey]) { delete json2[sessionKey]; } await fsP.writeFile(sessionsJsonPath, JSON.stringify(json2, null, 2), 'utf8'); sendJson(res, 200, { success: true }); } catch (error) { sendJson(res, 500, { success: false, error: String(error) }); } return true; } return false; }