@boundless-oss/atlas
Version:
Atlas - MCP Server for comprehensive startup project management
257 lines • 10.4 kB
JavaScript
import path from 'path';
import { NodeFileSystemAdapter } from './file-system-adapter.js';
export class ProcessStore {
dataPath = '';
configManager;
processCache = new Map();
executionCache = new Map();
fs;
constructor(configManager, fs) {
this.configManager = configManager;
this.fs = fs || new NodeFileSystemAdapter();
}
async init() {
const storageManager = this.configManager.getStorageManager();
this.dataPath = await storageManager.getModuleDataPath('process-automation', '');
// Ensure subdirectories exist
await this.fs.mkdir(path.join(this.dataPath, 'processes'), { recursive: true });
await this.fs.mkdir(path.join(this.dataPath, 'executions'), { recursive: true });
await this.fs.mkdir(path.join(this.dataPath, 'templates'), { recursive: true });
}
// Process Definition Methods
async saveProcess(process) {
const filePath = path.join(this.dataPath, 'processes', `${process.id}.json`);
await this.fs.writeFile(filePath, JSON.stringify(process, null, 2));
this.processCache.set(process.id, process);
}
async getProcess(processId) {
// Check cache first
if (this.processCache.has(processId)) {
return this.processCache.get(processId);
}
try {
const filePath = path.join(this.dataPath, 'processes', `${processId}.json`);
await this.fs.access(filePath);
const data = await this.fs.readFile(filePath, 'utf-8');
const process = JSON.parse(data);
this.processCache.set(processId, process);
return process;
}
catch (error) {
return null;
}
}
async getAllProcesses(filters) {
try {
const files = await this.fs.readdir(path.join(this.dataPath, 'processes'));
const processes = [];
for (const file of files) {
if (file.endsWith('.json')) {
const data = await this.fs.readFile(path.join(this.dataPath, 'processes', file), 'utf-8');
const process = JSON.parse(data);
// Apply filters
if (filters?.persona && process.persona !== filters.persona) {
continue;
}
if (filters?.hasEnabledTriggers) {
const hasEnabled = process.triggers.some(t => t.enabled);
if (!hasEnabled) {
continue;
}
}
processes.push(process);
}
}
return processes;
}
catch (error) {
return [];
}
}
async deleteProcess(processId) {
const filePath = path.join(this.dataPath, 'processes', `${processId}.json`);
await this.fs.unlink(filePath).catch(() => { }); // Ignore if doesn't exist
this.processCache.delete(processId);
}
// Execution Methods
async saveExecution(execution) {
// Ensure the process-specific execution directory exists
const processDir = path.join(this.dataPath, 'executions', execution.processId);
await this.fs.mkdir(processDir, { recursive: true });
const filePath = path.join(processDir, `${execution.id}.json`);
await this.fs.writeFile(filePath, JSON.stringify(execution, null, 2));
}
async getExecution(processId, executionId) {
try {
const filePath = path.join(this.dataPath, 'executions', processId, `${executionId}.json`);
const data = await this.fs.readFile(filePath, 'utf-8');
return JSON.parse(data);
}
catch (error) {
return null;
}
}
async getExecutionsForProcess(processId) {
try {
const processDir = path.join(this.dataPath, 'executions', processId);
const files = await this.fs.readdir(processDir);
const executions = [];
for (const file of files) {
if (file.endsWith('.json')) {
const data = await this.fs.readFile(path.join(processDir, file), 'utf-8');
const execution = JSON.parse(data);
executions.push(execution);
}
}
return executions.sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime());
}
catch (error) {
return [];
}
}
async getRecentExecutions(limit = 10) {
try {
const executionsDirs = await this.fs.readdir(path.join(this.dataPath, 'executions'));
const executions = [];
for (const processDir of executionsDirs) {
try {
const files = await this.fs.readdir(path.join(this.dataPath, 'executions', processDir));
for (const file of files) {
if (file.endsWith('.json')) {
const data = await this.fs.readFile(path.join(this.dataPath, 'executions', processDir, file), 'utf-8');
executions.push(JSON.parse(data));
}
}
}
catch {
// Skip if not a directory
}
}
return executions
.sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime())
.slice(0, limit);
}
catch (error) {
return [];
}
}
// Template Methods
async saveTemplate(template) {
const filePath = path.join(this.dataPath, 'templates', `${template.id}.json`);
await this.fs.writeFile(filePath, JSON.stringify(template, null, 2));
}
async getTemplate(templateId) {
try {
const filePath = path.join(this.dataPath, 'templates', `${templateId}.json`);
const data = await this.fs.readFile(filePath, 'utf-8');
return JSON.parse(data);
}
catch (error) {
return null;
}
}
async getTemplatesByPersona(persona) {
const allFiles = await this.fs.readdir(path.join(this.dataPath, 'templates'));
const templates = [];
for (const file of allFiles) {
if (file.endsWith('.json')) {
const data = await this.fs.readFile(path.join(this.dataPath, 'templates', file), 'utf-8');
const template = JSON.parse(data);
if (template.persona === persona) {
templates.push(template);
}
}
}
return templates;
}
// Metrics Methods
async getProcessMetrics(processId) {
const executions = await this.getExecutionsForProcess(processId);
const completed = executions.filter(e => e.status === 'completed');
const failed = executions.filter(e => e.status === 'failed');
const totalDuration = completed.reduce((sum, e) => sum + (e.duration || 0), 0);
const averageDuration = completed.length > 0 ? totalDuration / completed.length : 0;
return {
executionCount: executions.length,
successCount: completed.length,
failureCount: failed.length,
successRate: executions.length > 0 ? completed.length / executions.length : 0,
averageDuration,
lastExecutedAt: executions[0]?.startedAt
};
}
// Search and Filter Methods
async getProcessExecutions(processId, options) {
let executions = await this.getExecutionsForProcess(processId);
if (options?.status) {
executions = executions.filter(e => e.status === options.status);
}
if (options?.limit) {
executions = executions.slice(0, options.limit);
}
return executions;
}
async searchProcesses(query) {
const allProcesses = await this.getAllProcesses();
const lowerQuery = query.toLowerCase();
return allProcesses.filter(process => process.name.toLowerCase().includes(lowerQuery) ||
(process.description && process.description.toLowerCase().includes(lowerQuery)));
}
async getStats() {
const processes = await this.getAllProcesses();
const byPersona = {};
const byTriggerType = {};
let enabledTriggers = 0;
let totalTriggers = 0;
for (const process of processes) {
// Count by persona
const persona = process.persona || 'general';
byPersona[persona] = (byPersona[persona] || 0) + 1;
// Count triggers
for (const trigger of process.triggers) {
totalTriggers++;
if (trigger.enabled) {
enabledTriggers++;
}
byTriggerType[trigger.type] = (byTriggerType[trigger.type] || 0) + 1;
}
}
return {
totalProcesses: processes.length,
byPersona,
byTriggerType,
enabledTriggers,
totalTriggers
};
}
// Cleanup Methods
async cleanupOldExecutions(daysToKeep = 30) {
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);
try {
const processDirs = await this.fs.readdir(path.join(this.dataPath, 'executions'));
let deletedCount = 0;
for (const processDir of processDirs) {
try {
const files = await this.fs.readdir(path.join(this.dataPath, 'executions', processDir));
for (const file of files) {
if (file.endsWith('.json')) {
const filePath = path.join(this.dataPath, 'executions', processDir, file);
// For simplicity, delete all files in cleanup
await this.fs.unlink(filePath);
deletedCount++;
}
}
}
catch {
// Skip if not a directory
}
}
return deletedCount;
}
catch (error) {
return 0;
}
}
}
//# sourceMappingURL=process-store.js.map