vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
206 lines (205 loc) • 7.19 kB
JavaScript
import * as fs from 'fs/promises';
import * as path from 'path';
import logger from '../logger.js';
import { getProjectRoot } from '../tools/code-map-generator/utils/pathUtils.enhanced.js';
const DEFAULT_JOB_TIMEOUT_CONFIG = {
defaults: {
taskExecution: 300000,
llmRequest: 60000,
fileOperations: 10000,
networkOperations: 20000,
databaseOperations: 15000,
taskDecomposition: 600000,
recursiveTaskDecomposition: 720000,
taskRefinement: 180000,
agentCommunication: 30000
},
toolTimeouts: {},
retryPolicy: {
maxRetries: 3,
backoffMultiplier: 2.0,
initialDelayMs: 1000,
maxDelayMs: 30000,
enableExponentialBackoff: true,
retryableErrors: ['TIMEOUT', 'NETWORK_ERROR', 'RATE_LIMIT']
},
monitoring: {
enableTimeoutLogging: true,
logLevel: 'warn',
metricsEnabled: true
}
};
export class JobTimeoutConfigManager {
static instance = null;
config = null;
configPath;
initialized = false;
constructor() {
const projectRoot = getProjectRoot();
this.configPath = path.join(projectRoot, 'job-timeout-config.json');
}
static getInstance() {
if (!JobTimeoutConfigManager.instance) {
JobTimeoutConfigManager.instance = new JobTimeoutConfigManager();
}
return JobTimeoutConfigManager.instance;
}
async initialize() {
if (this.initialized) {
logger.debug('JobTimeoutConfigManager already initialized');
return;
}
try {
await this.loadConfig();
this.initialized = true;
logger.info('JobTimeoutConfigManager initialized successfully');
}
catch (error) {
logger.error({ error }, 'Failed to initialize JobTimeoutConfigManager');
this.config = DEFAULT_JOB_TIMEOUT_CONFIG;
this.initialized = true;
}
}
async loadConfig() {
try {
const configContent = await fs.readFile(this.configPath, 'utf-8');
const loadedConfig = JSON.parse(configContent);
this.config = {
...DEFAULT_JOB_TIMEOUT_CONFIG,
...loadedConfig,
defaults: {
...DEFAULT_JOB_TIMEOUT_CONFIG.defaults,
...loadedConfig.defaults
},
toolTimeouts: {
...loadedConfig.toolTimeouts
},
retryPolicy: {
...DEFAULT_JOB_TIMEOUT_CONFIG.retryPolicy,
...loadedConfig.retryPolicy
},
monitoring: {
...DEFAULT_JOB_TIMEOUT_CONFIG.monitoring,
...loadedConfig.monitoring
}
};
logger.info({
configPath: this.configPath,
toolCount: Object.keys(this.config.toolTimeouts).length
}, 'Loaded job timeout configuration');
}
catch (error) {
if (error.code === 'ENOENT') {
logger.warn({ configPath: this.configPath }, 'Job timeout config file not found, using defaults');
}
else {
logger.error({ error, configPath: this.configPath }, 'Error loading job timeout config');
}
this.config = DEFAULT_JOB_TIMEOUT_CONFIG;
}
}
getToolTimeoutConfig(toolName) {
if (!this.config) {
logger.warn('JobTimeoutConfigManager not initialized');
return null;
}
const toolConfig = this.config.toolTimeouts[toolName];
if (!toolConfig) {
logger.debug({ toolName }, 'No specific timeout config for tool, using defaults');
return null;
}
return toolConfig;
}
getTimeoutOperation(toolName) {
const toolConfig = this.getToolTimeoutConfig(toolName);
return toolConfig?.timeoutOperation || 'taskExecution';
}
getCustomTimeoutMs(toolName) {
const toolConfig = this.getToolTimeoutConfig(toolName);
return toolConfig?.customTimeoutMs;
}
getDefaultTimeout(operation) {
if (!this.config) {
return DEFAULT_JOB_TIMEOUT_CONFIG.defaults[operation] || 60000;
}
return this.config.defaults[operation] || 60000;
}
getRetryPolicy() {
if (!this.config) {
return DEFAULT_JOB_TIMEOUT_CONFIG.retryPolicy;
}
return this.config.retryPolicy;
}
getMonitoringConfig() {
if (!this.config) {
return DEFAULT_JOB_TIMEOUT_CONFIG.monitoring;
}
return this.config.monitoring;
}
isTimeoutLoggingEnabled() {
return this.getMonitoringConfig().enableTimeoutLogging;
}
areMetricsEnabled() {
return this.getMonitoringConfig().metricsEnabled;
}
getConfiguredTools() {
if (!this.config) {
return [];
}
return Object.keys(this.config.toolTimeouts);
}
validateConfig() {
const errors = [];
if (!this.config) {
errors.push('Configuration not loaded');
return { valid: false, errors };
}
const requiredDefaults = [
'taskExecution',
'llmRequest',
'fileOperations',
'networkOperations',
'databaseOperations'
];
for (const op of requiredDefaults) {
if (!this.config.defaults[op] || this.config.defaults[op] <= 0) {
errors.push(`Invalid or missing default timeout for ${op}`);
}
}
for (const [toolName, toolConfig] of Object.entries(this.config.toolTimeouts)) {
if (!toolConfig.timeoutOperation) {
errors.push(`Tool ${toolName} missing timeoutOperation`);
}
if (toolConfig.customTimeoutMs !== undefined && toolConfig.customTimeoutMs !== null && toolConfig.customTimeoutMs <= 0) {
errors.push(`Tool ${toolName} has invalid customTimeoutMs`);
}
}
if (this.config.retryPolicy.maxRetries < 0) {
errors.push('Invalid maxRetries in retry policy');
}
if (this.config.retryPolicy.backoffMultiplier < 1.0) {
errors.push('Invalid backoffMultiplier in retry policy');
}
return {
valid: errors.length === 0,
errors
};
}
async reload() {
logger.info('Reloading job timeout configuration');
await this.loadConfig();
}
getConfigSummary() {
return {
initialized: this.initialized,
configPath: this.configPath,
toolCount: this.config ? Object.keys(this.config.toolTimeouts).length : 0,
defaultTimeouts: this.config?.defaults || {},
retryEnabled: (this.config?.retryPolicy?.maxRetries ?? 0) > 0,
monitoringEnabled: this.config?.monitoring.metricsEnabled || false
};
}
}
export function getJobTimeoutConfigManager() {
return JobTimeoutConfigManager.getInstance();
}