UNPKG

autoagent-cli

Version:

Run autonomous AI agents using Claude or Gemini for task execution

329 lines (328 loc) • 13.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.ConfigManager = void 0; const fs_1 = require("fs"); const path = __importStar(require("path")); const os = __importStar(require("os")); const logger_1 = require("../utils/logger"); class ConfigManager { constructor(workingDir = process.cwd()) { this.workingDir = workingDir; this.globalConfigPath = path.join(ConfigManager.GLOBAL_CONFIG_DIR, ConfigManager.CONFIG_FILE); this.localConfigPath = path.join(this.workingDir, ConfigManager.LOCAL_CONFIG_DIR, ConfigManager.CONFIG_FILE); this.globalRateLimitPath = path.join(ConfigManager.GLOBAL_CONFIG_DIR, ConfigManager.RATE_LIMIT_FILE); this.localRateLimitPath = path.join(this.workingDir, ConfigManager.LOCAL_CONFIG_DIR, ConfigManager.RATE_LIMIT_FILE); this.config = { ...ConfigManager.DEFAULT_CONFIG }; } async loadConfig() { const globalConfig = await this.loadConfigFile(this.globalConfigPath); const localConfig = await this.loadConfigFile(this.localConfigPath); this.config = { ...ConfigManager.DEFAULT_CONFIG, ...globalConfig, ...localConfig }; if (process.env.AUTOAGENT_DEBUG === 'true') { logger_1.Logger.debug('Configuration loaded:'); logger_1.Logger.debug(` Default providers: ${JSON.stringify(ConfigManager.DEFAULT_CONFIG.providers)}`); logger_1.Logger.debug(` Global config providers: ${JSON.stringify(globalConfig.providers)}`); logger_1.Logger.debug(` Local config providers: ${JSON.stringify(localConfig.providers)}`); logger_1.Logger.debug(` Final providers: ${JSON.stringify(this.config.providers)}`); } return this.config; } getConfig() { return { ...this.config }; } async updateConfig(updates, scope = 'local') { const configPath = scope === 'global' ? this.globalConfigPath : this.localConfigPath; const currentConfig = await this.loadConfigFile(configPath); const updatedConfig = { ...currentConfig, ...updates }; await this.saveConfigFile(configPath, updatedConfig); await this.loadConfig(); } async isProviderRateLimited(provider) { const rateLimits = await this.loadRateLimits(); const providerData = rateLimits[provider]; if (!providerData || providerData.limitedAt === undefined) { return false; } const timeSinceLimited = Date.now() - providerData.limitedAt; const cooldownPeriod = this.config.rateLimitCooldown; return timeSinceLimited < cooldownPeriod; } async updateRateLimit(provider, isLimited) { const rateLimits = await this.loadRateLimits(); if (isLimited) { rateLimits[provider] = { limitedAt: Date.now(), attempts: (rateLimits[provider]?.attempts ?? 0) + 1 }; } else { delete rateLimits[provider]; } await this.saveRateLimits(rateLimits); } async getAvailableProviders() { const availableProviders = []; for (const provider of this.config.providers) { const isRateLimited = await this.isProviderRateLimited(provider); if (!isRateLimited) { availableProviders.push(provider); } } return availableProviders; } async checkRateLimit(provider) { const rateLimits = await this.loadRateLimits(); const providerData = rateLimits[provider]; if (!providerData || providerData.limitedAt === undefined) { return { isLimited: false }; } const timeSinceLimited = Date.now() - providerData.limitedAt; const cooldownPeriod = this.config.rateLimitCooldown; const isLimited = timeSinceLimited < cooldownPeriod; return { isLimited, timeRemaining: isLimited ? cooldownPeriod - timeSinceLimited : undefined, attempts: providerData.attempts }; } async loadConfigFile(filePath) { try { const content = await fs_1.promises.readFile(filePath, 'utf-8'); try { const config = JSON.parse(content); if (process.env.AUTOAGENT_DEBUG === 'true') { logger_1.Logger.debug(`Loaded config from ${filePath}: ${JSON.stringify(config)}`); } return config; } catch (parseError) { logger_1.Logger.warning(`Invalid JSON in config file ${filePath}: ${String(parseError)}`); return {}; } } catch (error) { if (process.env.AUTOAGENT_DEBUG === 'true') { logger_1.Logger.debug(`Config file not found: ${filePath}`); } return {}; } } async saveConfigFile(filePath, config) { await this.ensureDirectoryExists(path.dirname(filePath)); try { await fs_1.promises.writeFile(filePath, JSON.stringify(config, null, 2)); } catch (error) { if (error.code === 'EACCES' || error.code === 'EPERM') { throw new Error(`Permission denied: Cannot write to ${filePath}`); } throw error; } } async loadRateLimits() { try { const content = await fs_1.promises.readFile(this.localRateLimitPath, 'utf-8'); return JSON.parse(content); } catch { try { const content = await fs_1.promises.readFile(this.globalRateLimitPath, 'utf-8'); return JSON.parse(content); } catch { return {}; } } } async saveRateLimits(rateLimits) { await this.ensureDirectoryExists(path.dirname(this.localRateLimitPath)); await fs_1.promises.writeFile(this.localRateLimitPath, JSON.stringify(rateLimits, null, 2)); } async initConfig(global = false) { const configPath = global ? this.globalConfigPath : this.localConfigPath; await this.ensureDirectoryExists(path.dirname(configPath)); try { await fs_1.promises.access(configPath); throw new Error(`Configuration already exists at ${configPath}`); } catch (error) { if (error.code === 'ENOENT') { await this.saveConfigFile(configPath, ConfigManager.DEFAULT_CONFIG); } else { throw error; } } } async setProvider(provider, global = false) { if (!['claude', 'gemini'].includes(provider)) { throw new Error(`Invalid provider: ${provider}. Must be 'claude' or 'gemini'`); } await this.updateConfig({ providers: [provider] }, global ? 'global' : 'local'); } async setFailoverProviders(providers, global = false) { const validProviders = providers.filter(p => ['claude', 'gemini'].includes(p)); if (validProviders.length === 0) { throw new Error('No valid providers specified. Valid providers are: claude, gemini'); } await this.updateConfig({ providers: validProviders }, global ? 'global' : 'local'); } async clearRateLimit(provider) { const rateLimits = await this.loadRateLimits(); delete rateLimits[provider]; await this.saveRateLimits(rateLimits); } async saveConfig(config, global = false) { const configPath = global ? this.globalConfigPath : this.localConfigPath; await this.saveConfigFile(configPath, config); await this.loadConfig(); } async showConfig() { await this.loadConfig(); logger_1.Logger.info('\nšŸ“‹ Current Configuration:'); logger_1.Logger.info('─'.repeat(40)); logger_1.Logger.info('\nEffective Configuration:'); logger_1.Logger.info(JSON.stringify(this.config, null, 2)); logger_1.Logger.info('\nšŸ“ Configuration Sources:'); try { const globalConfig = await this.loadConfigFile(this.globalConfigPath); if (Object.keys(globalConfig).length > 0) { logger_1.Logger.info(`\nGlobal (${this.globalConfigPath}):`); logger_1.Logger.info(JSON.stringify(globalConfig, null, 2)); } } catch { logger_1.Logger.info('\nGlobal: Not configured'); } try { const localConfig = await this.loadConfigFile(this.localConfigPath); if (Object.keys(localConfig).length > 0) { logger_1.Logger.info(`\nLocal (${this.localConfigPath}):`); logger_1.Logger.info(JSON.stringify(localConfig, null, 2)); } } catch { logger_1.Logger.info('\nLocal: Not configured'); } logger_1.Logger.info('\nā±ļø Rate Limit Status:'); for (const provider of ['claude', 'gemini']) { const status = await this.checkRateLimit(provider); if (status.isLimited === true) { const remainingTime = Math.ceil((status.timeRemaining ?? 0) / 1000 / 60); logger_1.Logger.info(`${provider}: Rate limited (${remainingTime} minutes remaining)`); } else { logger_1.Logger.info(`${provider}: Not rate limited`); } } } async ensureDirectoryExists(dirPath) { try { await fs_1.promises.mkdir(dirPath, { recursive: true }); } catch (error) { if (error.code !== 'EEXIST') { throw error; } } } async resolveAdditionalDirectories(configDirs = [], cliDirs = [], basePath = this.workingDir) { const allDirs = [...configDirs, ...cliDirs]; const resolvedDirs = []; for (const dir of allDirs) { try { const resolvedPath = path.isAbsolute(dir) ? dir : path.resolve(basePath, dir); const stats = await fs_1.promises.stat(resolvedPath); if (!stats.isDirectory()) { logger_1.Logger.error(`Skipping ${dir}: not a directory`); continue; } if (this.isSystemDirectory(resolvedPath)) { logger_1.Logger.error(`Skipping ${dir}: system directory access not allowed`); continue; } resolvedDirs.push(resolvedPath); logger_1.Logger.debug(`Added additional directory: ${resolvedPath}`); } catch (error) { logger_1.Logger.error(`Skipping ${dir}: ${error instanceof Error ? error.message : 'Unknown error'}`); } } return resolvedDirs; } isSystemDirectory(dirPath) { const systemDirs = [ '/etc', '/usr', '/var', '/bin', '/sbin', '/boot', '/root', '/sys', '/proc', '/dev' ]; const normalizedPath = path.resolve(dirPath); return systemDirs.some(sysDir => normalizedPath.startsWith(sysDir + path.sep) || normalizedPath === sysDir); } } exports.ConfigManager = ConfigManager; ConfigManager.DEFAULT_CONFIG = { providers: ['claude', 'gemini'], failoverDelay: 5000, retryAttempts: 3, maxTokens: 100000, rateLimitCooldown: 3600000, logLevel: 'info', customInstructions: '', additionalDirectories: [], strictCompletion: false, completionConfidence: 70, ignoreToolFailures: false, maxRetryAttempts: 1 }; ConfigManager.GLOBAL_CONFIG_DIR = path.join(os.homedir(), '.autoagent'); ConfigManager.LOCAL_CONFIG_DIR = '.autoagent'; ConfigManager.CONFIG_FILE = 'config.json'; ConfigManager.RATE_LIMIT_FILE = 'rate-limits.json';