import fs from 'node:fs/promises'; import path from 'node:path'; import { statePath } from './runtime-paths.js'; export const DELETED_MESSAGES_PATH = statePath('deleted-messages.json'); export const HIDDEN_SESSIONS_PATH = statePath('hidden-sessions.json'); export function emptyDeletedMessagesState() { return { version: 1, sessions: {} }; } export function emptyHiddenSessionsState() { return { version: 1, sessions: {} }; } export async function readDeletedMessagesState() { try { const raw = await fs.readFile(DELETED_MESSAGES_PATH, 'utf8'); const parsed = JSON.parse(raw); return { version: 1, sessions: parsed && typeof parsed.sessions === 'object' && !Array.isArray(parsed.sessions) ? parsed.sessions : {} }; } catch (error) { if (error.code !== 'ENOENT') { console.warn('[sessions] Failed to read deleted message state:', error.message); } return emptyDeletedMessagesState(); } } export async function writeDeletedMessagesState(state) { await fs.mkdir(path.dirname(DELETED_MESSAGES_PATH), { recursive: true }); await fs.writeFile( DELETED_MESSAGES_PATH, JSON.stringify({ version: 1, sessions: state.sessions || {} }, null, 2), 'utf8' ); } export async function readHiddenSessionsState() { try { const raw = await fs.readFile(HIDDEN_SESSIONS_PATH, 'utf8'); const parsed = JSON.parse(raw); return { version: 1, sessions: parsed && typeof parsed.sessions === 'object' && !Array.isArray(parsed.sessions) ? parsed.sessions : {} }; } catch (error) { if (error.code !== 'ENOENT') { console.warn('[sessions] Failed to read hidden session state:', error.message); } return emptyHiddenSessionsState(); } } export async function writeHiddenSessionsState(state) { await fs.mkdir(path.dirname(HIDDEN_SESSIONS_PATH), { recursive: true }); await fs.writeFile( HIDDEN_SESSIONS_PATH, JSON.stringify({ version: 1, sessions: state.sessions || {} }, null, 2), 'utf8' ); } export async function readHiddenSessionIds() { const state = await readHiddenSessionsState(); return new Set(Object.keys(state.sessions || {})); } export async function hideSessionInMobile(session) { const id = String(session?.id || '').trim(); if (!id) { const error = new Error('Session id is required'); error.statusCode = 400; throw error; } const state = await readHiddenSessionsState(); const existing = state.sessions[id]; state.sessions[id] = { hiddenAt: existing?.hiddenAt || new Date().toISOString(), projectId: session.projectId || existing?.projectId || null, projectPath: session.cwd || existing?.projectPath || null, title: session.title || existing?.title || null }; await writeHiddenSessionsState(state); return { sessionId: id, hiddenAt: state.sessions[id].hiddenAt }; } export async function readDeletedMessageIds(sessionId) { const id = String(sessionId || '').trim(); if (!id) { return new Set(); } const state = await readDeletedMessagesState(); return new Set(Object.keys(state.sessions?.[id] || {})); } export function filterDeletedMessages(messages, deletedIds) { if (!deletedIds.size) { return messages; } return messages.filter((message) => !deletedIds.has(String(message.id || ''))); } export async function hideSessionMessage(sessionId, messageId) { const id = String(sessionId || '').trim(); const itemId = String(messageId || '').trim(); if (!id || !itemId) { const error = new Error('sessionId and messageId are required'); error.statusCode = 400; throw error; } const state = await readDeletedMessagesState(); if (!state.sessions[id] || typeof state.sessions[id] !== 'object' || Array.isArray(state.sessions[id])) { state.sessions[id] = {}; } const existing = state.sessions[id][itemId]; const deletedAt = existing?.deletedAt || new Date().toISOString(); state.sessions[id][itemId] = { deletedAt }; await writeDeletedMessagesState(state); return { sessionId: id, messageId: itemId, deletedAt }; }