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
JavaScript
/**
* 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