Slowtime MCP Server

by bmorphism
Verified
import { AsyncDuckDB, DuckDBConfig } from '@duckdb/duckdb-wasm'; import * as duckdb from '@duckdb/duckdb-wasm'; export interface TimeVaultEntry { id: string; encryptedData: string; roundNumber: number; createdAt: number; decryptedAt?: number; intervalId: string; metadata?: string; } export class TimeVault { private db: AsyncDuckDB | null = null; private initialized = false; private async initDB() { if (this.initialized) return; // Initialize the DuckDB WASM instance const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles(); const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES); const worker = new Worker(bundle.mainWorker!); const logger = new duckdb.ConsoleLogger(); const config: DuckDBConfig = { mainModule: bundle.mainModule, mainWorker: worker, logger: logger, }; this.db = new AsyncDuckDB(config); await this.db.instantiate(); // Create tables if they don't exist const conn = await this.db.connect(); await conn.query(` CREATE TABLE IF NOT EXISTS timevaults ( id VARCHAR PRIMARY KEY, encrypted_data TEXT NOT NULL, round_number BIGINT NOT NULL, created_at TIMESTAMP NOT NULL, decrypted_at TIMESTAMP, interval_id VARCHAR NOT NULL, metadata JSON ); CREATE INDEX IF NOT EXISTS idx_interval_id ON timevaults(interval_id); CREATE INDEX IF NOT EXISTS idx_created_at ON timevaults(created_at); `); await conn.close(); this.initialized = true; } async storeVault(entry: TimeVaultEntry): Promise<void> { await this.initDB(); const conn = await this.db!.connect(); try { await conn.query(` INSERT INTO timevaults ( id, encrypted_data, round_number, created_at, interval_id, metadata ) VALUES ( $1, $2, $3, $4, $5, $6 ) `, [ entry.id, entry.encryptedData, entry.roundNumber, new Date(entry.createdAt).toISOString(), entry.intervalId, entry.metadata || null ]); } finally { await conn.close(); } } async markDecrypted(id: string): Promise<void> { await this.initDB(); const conn = await this.db!.connect(); try { await conn.query(` UPDATE timevaults SET decrypted_at = $1 WHERE id = $2 `, [new Date().toISOString(), id]); } finally { await conn.close(); } } async getVault(id: string): Promise<TimeVaultEntry | null> { await this.initDB(); const conn = await this.db!.connect(); try { const result = await conn.query(` SELECT id, encrypted_data as encryptedData, round_number as roundNumber, EXTRACT(EPOCH FROM created_at) * 1000 as createdAt, CASE WHEN decrypted_at IS NOT NULL THEN EXTRACT(EPOCH FROM decrypted_at) * 1000 END as decryptedAt, interval_id as intervalId, metadata FROM timevaults WHERE id = $1 `, [id]); if (result.length === 0) return null; return result[0] as TimeVaultEntry; } finally { await conn.close(); } } async listVaults(options: { intervalId?: string; decryptedOnly?: boolean; limit?: number; offset?: number; } = {}): Promise<TimeVaultEntry[]> { await this.initDB(); const conn = await this.db!.connect(); try { let query = ` SELECT id, encrypted_data as encryptedData, round_number as roundNumber, EXTRACT(EPOCH FROM created_at) * 1000 as createdAt, CASE WHEN decrypted_at IS NOT NULL THEN EXTRACT(EPOCH FROM decrypted_at) * 1000 END as decryptedAt, interval_id as intervalId, metadata FROM timevaults WHERE 1=1 `; const params: any[] = []; let paramIndex = 1; if (options.intervalId) { query += ` AND interval_id = $${paramIndex++}`; params.push(options.intervalId); } if (options.decryptedOnly) { query += ` AND decrypted_at IS NOT NULL`; } query += ` ORDER BY created_at DESC`; if (options.limit) { query += ` LIMIT $${paramIndex++}`; params.push(options.limit); } if (options.offset) { query += ` OFFSET $${paramIndex++}`; params.push(options.offset); } const result = await conn.query(query, params); return result as TimeVaultEntry[]; } finally { await conn.close(); } } async getStats(): Promise<{ totalVaults: number; decryptedVaults: number; avgDecryptionTime?: number; }> { await this.initDB(); const conn = await this.db!.connect(); try { const result = await conn.query(` SELECT COUNT(*) as total, COUNT(decrypted_at) as decrypted, AVG( CASE WHEN decrypted_at IS NOT NULL THEN EXTRACT(EPOCH FROM (decrypted_at - created_at)) END ) as avg_time FROM timevaults `); return { totalVaults: Number(result[0].total), decryptedVaults: Number(result[0].decrypted), avgDecryptionTime: result[0].avg_time ? Number(result[0].avg_time) : undefined }; } finally { await conn.close(); } } async cleanup(maxAgeMs: number): Promise<void> { await this.initDB(); const conn = await this.db!.connect(); try { const cutoff = new Date(Date.now() - maxAgeMs).toISOString(); await conn.query(` DELETE FROM timevaults WHERE decrypted_at IS NOT NULL AND decrypted_at < $1 `, [cutoff]); } finally { await conn.close(); } } } // Create a singleton instance export const timeVault = new TimeVault();