UNPKG

@agenteract/cli

Version:

The unified command-line interface for Agenteract

118 lines (117 loc) 4.17 kB
// packages/cli/src/config.ts // todo(mribbons): combine with ../packages/agents/src/config.ts import fs from 'fs/promises'; import path from 'path'; export class MissingConfigError extends Error { constructor(message) { super(message); this.name = 'MissingConfigError'; } } export async function loadConfig(rootDir) { const configPath = path.join(rootDir, 'agenteract.config.js'); try { await fs.access(configPath); } catch (error) { throw new MissingConfigError('Agenteract config file not found'); } // In a Jest environment, dynamic import() of file URLs can be tricky. // A simple and effective workaround is to read the file and evaluate it. // This avoids the module resolution issues within the test runner. const configContent = await fs.readFile(configPath, 'utf-8'); // A simple regex to extract the default export object. // This is not a full parser, but it's robust enough for our config file format. const match = configContent.match(/export default (\{[\s\S]*\});/); if (!match) { console.error(`configContent: ${configContent}`); throw new Error('Could not parse agenteract.config.js. Make sure it has a default export.'); } // We can use Function to evaluate the object literal. // It's safer than eval() because it doesn't have access to the outer scope. return new Function(`return ${match[1]}`)(); } /** * Find the root directory containing agenteract.config.js * Searches upward from the current working directory */ export async function findConfigRoot(startDir = process.cwd()) { let currentDir = startDir; const root = path.parse(currentDir).root; while (currentDir !== root) { const configPath = path.join(currentDir, 'agenteract.config.js'); try { await fs.access(configPath); return currentDir; } catch { currentDir = path.dirname(currentDir); } } return null; } /** * Get the URL for the agent server */ export function getAgentServerUrl(config) { return `http://localhost:${config.port}`; } /** * Get the URL for a project's dev server */ export function getProjectServerUrl(config, projectName) { const project = config.projects.find(p => p.name === projectName); if (!project || !project.ptyPort) { return null; } return `http://localhost:${project.ptyPort}`; } /** * Get the URL for a dev server by type */ export function getDevServerUrlByType(config, type) { const project = config.projects.find(p => p.type === type && p.ptyPort); if (!project) { return null; } return `http://localhost:${project.ptyPort}`; } export async function addConfig(rootDir, projectPath, name, type) { const configPath = path.join(rootDir, 'agenteract.config.js'); let config; try { config = await loadConfig(rootDir); } catch (error) { if (error instanceof MissingConfigError) { config = { port: 8766, projects: [] }; } else { // For other errors (like parsing), we should not proceed. throw error; } } config.projects = config.projects || []; let nameExists = config.projects.find((p) => p.name === name); let pathExists = config.projects.find((p) => p.path === projectPath); if ((nameExists || pathExists) && nameExists !== pathExists) { console.error('project name and path exist across multiple projects. Please use a different name or path.'); console.error(`name: ${name}, path: ${projectPath}`); return; } let update = nameExists || pathExists; // if the project already exists, update it if (update) { update.path = projectPath; update.name = name; update.type = type; } else { let ptyPort = 8790; while (config.projects.some((p) => p.ptyPort === ptyPort)) { ptyPort++; } config.projects.push({ name, path: projectPath, type, ptyPort }); } await fs.writeFile(configPath, `export default ${JSON.stringify(config, null, 2)};`); }