import * as express from 'express'; import * as fs from 'fs'; import * as path from 'path'; // Define handler types to match project patterns type RouteHandler = (req: express.Request, res: express.Response) => any; /** * Handles API endpoints for dynamic tools created by the agent */ export function setupDynamicToolsAPI(app: express.Express): void { // Directory where dynamic tools will be stored const dynamicToolsDir = path.join(process.cwd(), 'src', 'client', 'views', 'nodes', 'chatbot', 'tools', 'dynamic'); console.log(`Dynamic tools directory path: ${dynamicToolsDir}`); // Ensure directory exists if (!fs.existsSync(dynamicToolsDir)) { try { fs.mkdirSync(dynamicToolsDir, { recursive: true }); console.log(`Created dynamic tools directory at ${dynamicToolsDir}`); } catch (error) { console.error(`Failed to create dynamic tools directory: ${error}`); } } /** * Save a dynamic tool to the server */ const saveDynamicTool: RouteHandler = (req, res) => { try { const { toolName, toolCode } = req.body; if (!toolName || !toolCode) { return res.status(400).json({ success: false, error: 'Missing toolName or toolCode in request body', }); } // Validate the tool name (should be PascalCase) if (!/^[A-Z][a-zA-Z0-9]*$/.test(toolName)) { return res.status(400).json({ success: false, error: 'Tool name must be in PascalCase format', }); } // Create the file path const filePath = path.join(dynamicToolsDir, `${toolName}.ts`); // Check if file already exists and is different let existingCode = ''; if (fs.existsSync(filePath)) { existingCode = fs.readFileSync(filePath, 'utf8'); } // Only write if the file doesn't exist or the content is different if (existingCode !== toolCode) { fs.writeFileSync(filePath, toolCode, 'utf8'); console.log(`Saved dynamic tool: ${toolName}`); } else { console.log(`Dynamic tool ${toolName} already exists with the same content`); } return res.json({ success: true, toolName }); } catch (error) { console.error('Error saving dynamic tool:', error); return res.status(500).json({ success: false, error: error instanceof Error ? error.message : 'Unknown error', }); } }; /** * Get a list of all available dynamic tools */ const getDynamicTools: RouteHandler = (req, res) => { try { // Get all TypeScript files in the dynamic tools directory const files = fs .readdirSync(dynamicToolsDir) .filter(file => file.endsWith('.ts')) .map(file => ({ name: path.basename(file, '.ts'), path: path.join('dynamic', file), })); return res.json({ success: true, tools: files }); } catch (error) { console.error('Error getting dynamic tools:', error); return res.status(500).json({ success: false, error: error instanceof Error ? error.message : 'Unknown error', }); } }; /** * Get the code for a specific dynamic tool */ const getDynamicTool: RouteHandler = (req, res) => { try { const { toolName } = req.params; const filePath = path.join(dynamicToolsDir, `${toolName}.ts`); if (!fs.existsSync(filePath)) { return res.status(404).json({ success: false, error: `Tool ${toolName} not found`, }); } const toolCode = fs.readFileSync(filePath, 'utf8'); return res.json({ success: true, toolName, toolCode }); } catch (error) { console.error('Error getting dynamic tool:', error); return res.status(500).json({ success: false, error: error instanceof Error ? error.message : 'Unknown error', }); } }; // Register routes app.post('/saveDynamicTool', saveDynamicTool); app.get('/getDynamicTools', getDynamicTools); app.get('/getDynamicTool/:toolName', getDynamicTool); }