import { existsSync, mkdirSync } from 'fs'; import { homedir } from 'os'; import { join } from 'path'; import Database from 'better-sqlite3'; const DATA_DIR = process.env.AGENT_DATA_DIR || join(homedir(), '.local', 'share', 'agent-session-manager'); const DB_PATH = join(DATA_DIR, 'metadata.sqlite'); let db = null; let ready = false; function ensureDir() { if (!existsSync(DATA_DIR)) { mkdirSync(DATA_DIR, { recursive: true }); } } export function initMetadataDb() { ensureDir(); try { db = new Database(DB_PATH); db.pragma('journal_mode = WAL'); db.exec(` CREATE TABLE IF NOT EXISTS session_names ( engine TEXT NOT NULL, session_id TEXT NOT NULL, custom_name TEXT NOT NULL DEFAULT '', updated_at INTEGER NOT NULL DEFAULT (strftime('%s','now')), PRIMARY KEY (engine, session_id) ) `); ready = true; console.log('[MetadataDB] Connected to', DB_PATH); } catch (err) { console.warn('[MetadataDB] Init error:', err.message); ready = false; } } export function closeMetadataDb() { if (db) { try { db.close(); } catch {} db = null; ready = false; } } /** * Set or clear a custom name for a session. * @param {string} engine - 'codex' | 'claude' | 'agy' * @param {string} sessionId * @param {string|null} name - null or empty string to delete */ export function setCustomName(engine, sessionId, name) { if (!ready || !db) return false; const trimmed = (name || '').trim(); if (!trimmed) { // Delete db.prepare('DELETE FROM session_names WHERE engine = ? AND session_id = ?').run(engine, sessionId); return true; } if (trimmed.length > 100) return false; db.prepare(` INSERT INTO session_names (engine, session_id, custom_name, updated_at) VALUES (?, ?, ?, strftime('%s','now')) ON CONFLICT(engine, session_id) DO UPDATE SET custom_name = excluded.custom_name, updated_at = strftime('%s','now') `).run(engine, sessionId, trimmed); return true; } export function deleteCustomName(engine, sessionId) { if (!ready || !db) return false; db.prepare('DELETE FROM session_names WHERE engine = ? AND session_id = ?').run(engine, sessionId); return true; } /** * Get custom name for a session. */ export function getCustomName(engine, sessionId) { if (!ready || !db) return null; const row = db.prepare('SELECT custom_name FROM session_names WHERE engine = ? AND session_id = ?').get(engine, sessionId); return row ? row.custom_name : null; } /** * Get custom names for multiple sessions, keyed by "engine:id". */ export function getCustomNames(sessions) { if (!ready || !db || !sessions.length) return {}; const result = {}; // Build a map of unique (engine, session_id) pairs const pairs = []; const seen = new Set(); for (const s of sessions) { const key = `${s.engine}:${s.id}`; if (seen.has(key)) continue; seen.add(key); pairs.push([s.engine, s.id]); } if (!pairs.length) return result; // Use individual queries (simple, correct) const stmt = db.prepare('SELECT custom_name FROM session_names WHERE engine = ? AND session_id = ?'); for (const [engine, id] of pairs) { const row = stmt.get(engine, id); if (row && row.custom_name) { result[`${engine}:${id}`] = row.custom_name; } } return result; } export function isMetadataReady() { return ready; }