import { CodexAdapter } from './codex-adapter.js'; import { ClaudeAdapter } from './claude-adapter.js'; import { AgyAdapter } from './agy-adapter.js'; import { OpenCodeAdapter } from './opencode-adapter.js'; import { ReasonixAdapter } from './reasonix-adapter.js'; const ENGINES = { codex: new CodexAdapter(), claude: new ClaudeAdapter(), agy: new AgyAdapter(), opencode: new OpenCodeAdapter(), reasonix: new ReasonixAdapter(), }; export function getEngine(name) { return ENGINES[name] || null; } export function getAllEngines() { return Object.values(ENGINES); } export function getAvailableEngines() { return Object.values(ENGINES).filter(e => e.available); } /** * Refresh specified engines (or all if not specified). * @param {string[]} [engineNames] - Optional list of engine names to refresh */ export async function refreshEngines(engineNames = null) { const names = engineNames || Object.keys(ENGINES); const results = {}; for (const name of names) { const engine = ENGINES[name]; if (engine) { try { results[name] = await engine.refresh(); } catch (err) { results[name] = { available: false, session_count: 0, error: err.message }; } } } return results; } /** * List sessions across all engines or a specific engine, sorted by created_at desc. */ export function listSessions({ engine = 'all', query, cwd, model, source, status, from, to, limit, offset } = {}) { const engines = engine === 'all' ? getAllEngines() : [getEngine(engine)].filter(Boolean); const allSessions = []; for (const e of engines) { if (!e.available) continue; try { const result = e.listSessions({ query, cwd, model, source, status, from, to, limit: null, offset: 0 }); allSessions.push(...result.sessions); } catch (err) { console.error(`[Registry] listSessions error for ${e.name}:`, err.message); } } // Global sort by created_at desc allSessions.sort((a, b) => (b.created_at || 0) - (a.created_at || 0)); const total = allSessions.length; const off = offset || 0; const lim = (limit === null || limit === undefined) ? total : Math.min(limit, 200); return { sessions: allSessions.slice(off, off + lim), total }; } /** * Get a single session by engine + id. */ export function getSession(engine, id) { const e = getEngine(engine); if (!e) return null; return e.getSession(id); } /** * Get messages for a session by engine + id. */ export async function getSessionMessages(engine, id) { const e = getEngine(engine); if (!e) return []; return await e.getMessages(id); } /** * Get aggregate tool usage across engines. */ export function getToolUsage({ engine = 'all' } = {}) { const engines = engine === 'all' ? getAllEngines() : [getEngine(engine)].filter(Boolean); const allTools = []; for (const e of engines) { if (!e.available) continue; const tools = e.getToolUsage(); for (const t of tools) { // Add engine info t.engine = e.name; t.engine_label = e.label; } allTools.push(...tools); } // Merge same-named tools across engines const merged = new Map(); for (const t of allTools) { const key = t.name; if (!merged.has(key)) { merged.set(key, { name: key, total_calls: 0, session_count: 0, engines: [], sessions: [] }); } const m = merged.get(key); m.total_calls += t.total_calls; m.session_count += t.session_count; if (!m.engines.includes(t.engine_label)) { m.engines.push(t.engine_label); } m.sessions.push(...t.sessions); } return Array.from(merged.values()).sort((a, b) => b.total_calls - a.total_calls); } /** * Resolve a short ID prefix across all or a specific engine. */ export function resolvePrefix(prefix, engine = 'all') { const engines = engine === 'all' ? getAllEngines() : [getEngine(engine)].filter(Boolean); const allMatches = []; for (const e of engines) { if (!e.available) continue; try { allMatches.push(...e.resolvePrefix(prefix)); } catch {} } return { matches: allMatches }; } /** * Get health info for all engines. */ export function getAllHealth() { const info = {}; for (const [name, engine] of Object.entries(ENGINES)) { info[name] = engine.getHealthInfo(); } return info; } export { ENGINES };