UNPKG

@tb.p/terminai

Version:

MCP (Model Context Protocol) server for secure SSH remote command execution. Enables AI assistants like Claude, Cursor, and VS Code to execute commands on remote servers via SSH with command validation, history tracking, and web-based configuration UI.

86 lines (66 loc) 2.27 kB
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs'; import { resolve, dirname } from 'path'; import { homedir } from 'os'; import { HistorySchema } from './schema.js'; function getHistoryPath() { const defaultDir = resolve(homedir(), '.config', 'terminai'); return resolve(defaultDir, 'history.json'); } function loadHistory() { const historyPath = getHistoryPath(); if (!existsSync(historyPath)) { return { connections: {} }; } try { const rawHistory = JSON.parse(readFileSync(historyPath, 'utf-8')); return HistorySchema.parse(rawHistory); } catch (error) { console.error(`Failed to load history: ${error.message}`); return { connections: {} }; } } function saveHistory(history) { const historyPath = getHistoryPath(); const validatedHistory = HistorySchema.parse(history); const historyDir = dirname(historyPath); if (!existsSync(historyDir)) { mkdirSync(historyDir, { recursive: true }); } writeFileSync(historyPath, JSON.stringify(validatedHistory, null, 2), 'utf-8'); } export function appendHistory(connectionName, entry) { const history = loadHistory(); if (!history.connections[connectionName]) { history.connections[connectionName] = []; } history.connections[connectionName].push({ ...entry, timestamp: new Date().toISOString(), }); saveHistory(history); } export function getHistory(connectionName, limit = 50) { const history = loadHistory(); const entries = history.connections[connectionName] || []; return entries.slice(-limit); } export function clearHistory(connectionName) { const history = loadHistory(); if (connectionName) { delete history.connections[connectionName]; } else { history.connections = {}; } saveHistory(history); } export function pruneHistory(config) { const history = loadHistory(); const retentionMs = config.global.historyRetentionDays * 24 * 60 * 60 * 1000; const cutoffDate = new Date(Date.now() - retentionMs); for (const connectionName in history.connections) { history.connections[connectionName] = history.connections[connectionName] .filter(entry => new Date(entry.timestamp) > cutoffDate) .slice(-config.global.historyLimit); } saveHistory(history); }