UNPKG

ai-debug-local-mcp

Version:

🎯 ENHANCED AI GUIDANCE v4.1.2: Dramatically improved tool descriptions help AI users choose the right tools instead of 'close enough' options. Ultra-fast keyboard automation (10x speed), universal recording, multi-ecosystem debugging support, and compreh

232 lines • 8.65 kB
/** * Batch Automation Optimizer * Dramatically improves computer control performance through batching and parallelization */ import { exec } from 'child_process'; import { promisify } from 'util'; import { performanceProfiler } from './automation-performance-profiler.js'; const execAsync = promisify(exec); export class BatchAutomationOptimizer { actionQueue = []; isProcessing = false; cachedElements = new Map(); CACHE_TTL = 30000; // 30 seconds /** * Add action to batch queue */ queueAction(action) { this.actionQueue.push(action); } /** * Execute all queued actions as a single batch */ async executeBatch() { if (this.isProcessing) { throw new Error('Batch execution already in progress'); } this.isProcessing = true; const startTime = performance.now(); const results = []; const errors = []; try { // Group actions by type for optimal execution const groupedActions = this.groupActionsByType(this.actionQueue); // Execute each group in parallel where possible const promises = []; // Handle screenshots in parallel if (groupedActions.screenshots.length > 0) { promises.push(this.executeScreenshotsBatch(groupedActions.screenshots)); } // Combine all UI actions into single AppleScript if (groupedActions.uiActions.length > 0) { promises.push(this.executeUIActionsBatch(groupedActions.uiActions)); } // Execute parallel operations const batchResults = await Promise.all(promises); results.push(...batchResults.flat()); const duration = performance.now() - startTime; console.log(`âš¡ Batch execution completed in ${duration.toFixed(2)}ms (${this.actionQueue.length} actions)`); // Clear queue after successful execution this.actionQueue = []; return { success: true, duration, results, errors }; } catch (error) { errors.push(error instanceof Error ? error.message : String(error)); return { success: false, duration: performance.now() - startTime, results, errors }; } finally { this.isProcessing = false; } } /** * Group actions by type for optimal execution */ groupActionsByType(actions) { const screenshots = []; const uiActions = []; for (const action of actions) { if (action.type === 'screenshot') { screenshots.push(action); } else { uiActions.push(action); } } return { screenshots, uiActions }; } /** * Execute multiple screenshots in parallel */ async executeScreenshotsBatch(screenshots) { performanceProfiler.startOperation('batch-screenshots', 'screenshot', { count: screenshots.length }); const promises = screenshots.map(async (action) => { if (!action.path) throw new Error('Screenshot path required'); await execAsync(`screencapture -x ${action.path}`); return { type: 'screenshot', path: action.path }; }); const results = await Promise.all(promises); performanceProfiler.endOperation('batch-screenshots'); return results; } /** * Execute UI actions as a single AppleScript */ async executeUIActionsBatch(actions) { performanceProfiler.startOperation('batch-ui-actions', 'applescript', { count: actions.length }); // Build optimized AppleScript const script = this.buildOptimizedAppleScript(actions); try { const result = await execAsync(`osascript -e '${script}'`); performanceProfiler.endOperation('batch-ui-actions'); return [{ type: 'batch-ui', success: true, output: result.stdout }]; } catch (error) { performanceProfiler.endOperation('batch-ui-actions'); throw error; } } /** * Build optimized AppleScript combining multiple actions */ buildOptimizedAppleScript(actions) { const commands = ['tell application "System Events"']; for (const action of actions) { switch (action.type) { case 'click': if (action.target) { commands.push(` click at {${action.target.x}, ${action.target.y}}`); } break; case 'type': if (action.text) { commands.push(` keystroke "${action.text}"`); } break; case 'key': if (action.keyCode !== undefined) { const modifiers = action.modifiers ? ` using {${action.modifiers.join(', ')}}` : ''; commands.push(` key code ${action.keyCode}${modifiers}`); } break; case 'wait': if (action.duration) { commands.push(` delay ${action.duration / 1000}`); } break; case 'drag': if (action.from && action.to) { // Optimized drag using CGEvent would be faster, but for now use AppleScript commands.push(` do shell script "python3 -c \\" import Quartz import time event = Quartz.CGEventCreateMouseEvent(None, Quartz.kCGEventLeftMouseDown, (${action.from.x}, ${action.from.y}), Quartz.kCGMouseButtonLeft) Quartz.CGEventPost(Quartz.kCGHIDEventTap, event) time.sleep(0.1) event = Quartz.CGEventCreateMouseEvent(None, Quartz.kCGEventLeftMouseDragged, (${action.to.x}, ${action.to.y}), Quartz.kCGMouseButtonLeft) Quartz.CGEventPost(Quartz.kCGHIDEventTap, event) event = Quartz.CGEventCreateMouseEvent(None, Quartz.kCGEventLeftMouseUp, (${action.to.x}, ${action.to.y}), Quartz.kCGMouseButtonLeft) Quartz.CGEventPost(Quartz.kCGHIDEventTap, event) \\""`); } break; } } commands.push('end tell'); return commands.join('\\n'); } /** * Smart wait optimization - dynamic delays based on context */ async smartWait(context) { const delays = { app_launch: 3000, page_load: 2000, animation: 500, quick: 100 }; const delay = delays[context] || 1000; // Use smaller delay if we've cached that the operation is fast const cachedDelay = this.getCachedDelay(context); const actualDelay = cachedDelay || delay; performanceProfiler.startOperation(`smart-wait-${context}`, 'applescript', { delay: actualDelay }); await new Promise(resolve => setTimeout(resolve, actualDelay)); performanceProfiler.endOperation(`smart-wait-${context}`); } /** * Get cached delay for a context */ getCachedDelay(context) { // In a real implementation, this would track actual completion times // and adjust delays accordingly return null; } /** * Cache UI element coordinates to avoid repeated lookups */ cacheElementPosition(elementId, x, y) { this.cachedElements.set(elementId, { x, y, timestamp: Date.now() }); } /** * Get cached element position if still valid */ getCachedElementPosition(elementId) { const cached = this.cachedElements.get(elementId); if (!cached) return null; // Check if cache is still valid if (Date.now() - cached.timestamp > this.CACHE_TTL) { this.cachedElements.delete(elementId); return null; } return { x: cached.x, y: cached.y }; } /** * Clear expired cache entries */ cleanCache() { const now = Date.now(); for (const [key, value] of this.cachedElements.entries()) { if (now - value.timestamp > this.CACHE_TTL) { this.cachedElements.delete(key); } } } } // Export singleton instance export const batchAutomation = new BatchAutomationOptimizer(); //# sourceMappingURL=batch-automation-optimizer.js.map