123 lines
3.3 KiB
JavaScript
123 lines
3.3 KiB
JavaScript
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;
|
|
}
|