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
3,198 lines ⢠144 kB
JavaScript
/**
* System Automation Handler - Native system mouse and keyboard control
* Enables AI models to control system cursor for Vim, terminal apps, and desktop automation
*
* Uses nut.js (modern, fast) with robotjs fallback for maximum compatibility
*/
import { BaseToolHandler } from './base-handler.js';
import { batchAutomation } from '../utils/batch-automation-optimizer.js';
import { performanceProfiler } from '../utils/automation-performance-profiler.js';
import { nativeAutomation } from '../utils/native-automation-executor.js';
import { uiCache } from '../utils/advanced-ui-cache.js';
import { wsAutomationServer } from '../utils/websocket-automation-server.js';
import { EnhancedErrorContext } from './enhanced-error-context.js';
import { mouse, keyboard, screen, Button, Key } from '@nut-tree-fork/nut-js';
import { BackgroundWindowAutomation } from '../utils/background-window-automation.js';
import { accessibilityBridge } from '../utils/accessibility-bridge.js';
export class SystemAutomationHandler extends BaseToolHandler {
robotjsFallback = null;
macroSystem; // Initialized in constructor
backgroundAutomation;
tools = [
{
name: 'verify_window_focus',
description: 'šÆ VISUAL FEEDBACK: Verify which window has focus and show visual notification - essential before automation to ensure correct target. ā ļø USE THIS FIRST: Always verify window focus before any automation to prevent accidental clicks in wrong applications.',
inputSchema: {
type: 'object',
properties: {
highlightActive: {
type: 'boolean',
description: 'Show visual notification of active window (default: true)'
}
}
}
},
{
name: 'analyze_screen_visually',
description: 'šļø VISUAL AI ANALYSIS - Take screenshot and analyze what\'s visible on screen. Identify text, UI elements, colors, and provide visual context for intelligent automation decisions. š” BEST FOR: Getting visual context before automation - not for taking regular screenshots (use take_screenshot tools for that).',
inputSchema: {
type: 'object',
properties: {
action: {
type: 'string',
enum: ['full_screen', 'region', 'find_text', 'identify_elements', 'color_analysis'],
description: 'Type of visual analysis to perform'
},
region: {
type: 'object',
properties: {
x: { type: 'number', description: 'X coordinate of region' },
y: { type: 'number', description: 'Y coordinate of region' },
width: { type: 'number', description: 'Width of region' },
height: { type: 'number', description: 'Height of region' }
},
description: 'Screen region to analyze (for region analysis)'
},
searchText: {
type: 'string',
description: 'Text to search for on screen'
},
includeOCR: {
type: 'boolean',
description: 'Include text extraction (OCR) in analysis'
},
includeElements: {
type: 'boolean',
description: 'Include UI element detection'
},
includeColors: {
type: 'boolean',
description: 'Include color analysis'
}
},
required: ['action']
}
},
{
name: 'control_computer_batch',
description: 'ā” OPTIMIZED: Execute multiple computer control actions in a single batch for 3-5x performance improvement. šÆ WHEN TO USE: Multiple actions needed (3+ clicks/types/keys). For single actions, use control_computer_native instead.',
inputSchema: {
type: 'object',
properties: {
actions: {
type: 'array',
items: {
type: 'object',
properties: {
type: {
type: 'string',
enum: ['click', 'type', 'key', 'screenshot', 'wait', 'drag'],
description: 'Type of action to perform'
},
x: { type: 'number', description: 'X coordinate for click/drag' },
y: { type: 'number', description: 'Y coordinate for click/drag' },
text: { type: 'string', description: 'Text to type' },
keyCode: { type: 'number', description: 'Key code for keyboard action' },
modifiers: {
type: 'array',
items: { type: 'string' },
description: 'Modifier keys (command, shift, option, control)'
},
duration: { type: 'number', description: 'Duration in ms for wait' },
path: { type: 'string', description: 'Path for screenshot' },
fromX: { type: 'number', description: 'Starting X for drag' },
fromY: { type: 'number', description: 'Starting Y for drag' },
toX: { type: 'number', description: 'Ending X for drag' },
toY: { type: 'number', description: 'Ending Y for drag' }
},
required: ['type']
},
description: 'Array of actions to execute in batch'
},
parallel: {
type: 'boolean',
default: true,
description: 'Execute independent actions in parallel where possible'
}
},
required: ['actions']
}
},
{
name: 'control_computer_native',
description: 'š ULTRA-FAST: Native CGEvent automation - 10x faster than robotjs for single actions. šÆ WHEN TO USE: Single click, single key press, or single type action. For multiple actions (3+), use control_computer_batch instead. ā” SPEED: 10x faster than other keyboard tools.',
inputSchema: {
type: 'object',
properties: {
action: {
type: 'string',
enum: ['click', 'move', 'key', 'type', 'batch'],
description: 'Native action to perform'
},
x: { type: 'number', description: 'X coordinate' },
y: { type: 'number', description: 'Y coordinate' },
text: { type: 'string', description: 'Text to type' },
key: { type: 'string', description: 'Key to press' },
modifiers: {
type: 'array',
items: { type: 'string' },
description: 'Modifier keys'
},
actions: {
type: 'array',
description: 'Batch actions for native execution'
}
},
required: ['action']
}
},
{
name: 'manage_ui_cache',
description: 'š§ Advanced UI element caching with intelligent invalidation',
inputSchema: {
type: 'object',
properties: {
operation: {
type: 'string',
enum: ['get', 'set', 'invalidate', 'stats', 'clear', 'export', 'import'],
description: 'Cache operation'
},
elementId: { type: 'string', description: 'Element identifier' },
elementData: {
type: 'object',
description: 'Element data to cache'
},
pattern: { type: 'string', description: 'Pattern for invalidation' },
importData: { type: 'string', description: 'Cache data to import' }
},
required: ['operation']
}
},
{
name: 'websocket_automation_control',
description: 'ā” WebSocket-based zero-latency automation control',
inputSchema: {
type: 'object',
properties: {
command: {
type: 'string',
enum: ['start', 'stop', 'status', 'execute', 'stream'],
description: 'WebSocket server command'
},
action: {
type: 'object',
description: 'Action to execute via WebSocket'
},
streamType: {
type: 'string',
enum: ['performance', 'events', 'all'],
description: 'Type of data to stream'
}
},
required: ['command']
}
},
{
name: 'profile_automation_performance',
description: 'š¬ Profile and analyze computer control performance to identify bottlenecks',
inputSchema: {
type: 'object',
properties: {
compareMethod: {
type: 'boolean',
default: true,
description: 'Compare different automation methods'
},
realTimeMonitoring: {
type: 'boolean',
default: false,
description: 'Enable real-time performance monitoring'
}
}
}
},
{
name: 'control_system_mouse',
description: 'š±ļø Control the actual system mouse cursor with VISUAL FEEDBACK - click, move, drag, scroll anywhere on screen. Automatically captures before/after screenshots so AI can see what happened! Perfect for controlling Vim, terminal apps, and desktop automation.',
inputSchema: {
type: 'object',
properties: {
action: {
type: 'string',
enum: ['move', 'click', 'drag', 'scroll', 'doubleclick', 'rightclick'],
description: 'Mouse action to perform'
},
x: { type: 'number', description: 'X coordinate on screen' },
y: { type: 'number', description: 'Y coordinate on screen' },
startX: { type: 'number', description: 'Start X coordinate for drag actions' },
startY: { type: 'number', description: 'Start Y coordinate for drag actions' },
endX: { type: 'number', description: 'End X coordinate for drag actions' },
endY: { type: 'number', description: 'End Y coordinate for drag actions' },
button: { type: 'string', enum: ['left', 'right', 'middle'], description: 'Mouse button' },
scrollDirection: { type: 'string', enum: ['up', 'down', 'left', 'right'], description: 'Scroll direction' },
scrollAmount: { type: 'number', description: 'Scroll amount (default: 3)' },
delay: { type: 'number', description: 'Delay after action in ms (default: 100)' },
captureVisualFeedback: { type: 'boolean', description: 'Capture before/after screenshots (default: true)' },
analyzeRegion: {
type: 'object',
properties: {
width: { type: 'number', description: 'Region width around action point' },
height: { type: 'number', description: 'Region height around action point' }
},
description: 'Capture focused region around action point for detailed analysis'
}
},
required: ['action']
}
},
{
name: 'control_vim_terminal',
description: 'š„ļø Specialized Vim terminal control - combines mouse and keyboard for terminal Vim automation. Click lines, execute commands, scroll pages, and focus terminal windows.',
inputSchema: {
type: 'object',
properties: {
action: {
type: 'string',
enum: ['click_line', 'select_text', 'scroll_page', 'vim_command', 'focus_window'],
description: 'Vim-specific action to perform'
},
terminalBounds: {
type: 'object',
properties: {
x: { type: 'number', description: 'Terminal window X position' },
y: { type: 'number', description: 'Terminal window Y position' },
width: { type: 'number', description: 'Terminal window width' },
height: { type: 'number', description: 'Terminal window height' }
},
description: 'Terminal window boundaries for accurate positioning'
},
lineNumber: { type: 'number', description: 'Line number to click (1-based)' },
command: { type: 'string', description: 'Vim command to execute (without :)' },
text: { type: 'string', description: 'Text to select or work with' },
direction: { type: 'string', enum: ['up', 'down'], description: 'Direction for page scrolling' }
},
required: ['action']
}
},
{
name: 'control_system_screen',
description: 'š· Screen capture and analysis - take screenshots, get pixel colors, and analyze screen content for automation context.',
inputSchema: {
type: 'object',
properties: {
action: {
type: 'string',
enum: ['capture', 'find_text', 'find_color', 'get_pixel'],
description: 'Screen action to perform'
},
x: { type: 'number', description: 'X coordinate for pixel or region' },
y: { type: 'number', description: 'Y coordinate for pixel or region' },
width: { type: 'number', description: 'Width for screen capture region' },
height: { type: 'number', description: 'Height for screen capture region' },
text: { type: 'string', description: 'Text to find on screen (OCR)' },
color: { type: 'string', description: 'Color to find on screen (hex)' }
},
required: ['action']
}
},
{
name: 'swift_screenshot_capture',
description: 'š SWIFT NATIVE SCREENSHOTS: High-performance screenshot capture using native macOS ScreenCaptureKit. Faster than browser-based screenshots with full system access.',
inputSchema: {
type: 'object',
properties: {
action: {
type: 'string',
enum: ['full_screen', 'region', 'window', 'application', 'visible_windows'],
description: 'Type of screenshot to capture'
},
x: { type: 'number', description: 'X coordinate for region capture' },
y: { type: 'number', description: 'Y coordinate for region capture' },
width: { type: 'number', description: 'Width for region capture' },
height: { type: 'number', description: 'Height for region capture' },
windowId: { type: 'number', description: 'Window ID for window capture' },
bundleId: { type: 'string', description: 'Bundle ID for application capture' },
appName: { type: 'string', description: 'Application name for application capture' },
options: {
type: 'object',
properties: {
format: { type: 'string', enum: ['png', 'jpeg', 'jpg'], description: 'Image format' },
quality: { type: 'number', minimum: 0, maximum: 1, description: 'JPEG quality (0.0-1.0)' },
scale: { type: 'number', description: 'Scale factor (1.0, 2.0 for retina)' }
},
description: 'Screenshot options'
},
outputPath: { type: 'string', description: 'Optional output file path' }
},
required: ['action']
}
},
{
name: 'get_system_automation_info',
description: 'š Get system automation capabilities and current state - screen resolution, mouse position, available libraries, and platform information.',
inputSchema: {
type: 'object',
properties: {},
additionalProperties: false
}
},
{
name: 'analyze_screen_visually',
description: 'šļø VISUAL AI ANALYSIS - Take screenshot and analyze what\'s visible on screen. Identify text, UI elements, colors, and provide visual context for intelligent automation decisions.',
inputSchema: {
type: 'object',
properties: {
action: {
type: 'string',
enum: ['full_screen', 'region', 'find_text', 'identify_elements', 'color_analysis'],
description: 'Type of visual analysis to perform'
},
region: {
type: 'object',
properties: {
x: { type: 'number', description: 'X coordinate of region' },
y: { type: 'number', description: 'Y coordinate of region' },
width: { type: 'number', description: 'Width of region' },
height: { type: 'number', description: 'Height of region' }
},
description: 'Screen region to analyze (for region analysis)'
},
searchText: { type: 'string', description: 'Text to search for on screen' },
includeOCR: { type: 'boolean', description: 'Include text extraction (OCR) in analysis' },
includeColors: { type: 'boolean', description: 'Include color analysis' },
includeElements: { type: 'boolean', description: 'Include UI element detection' }
},
required: ['action']
}
},
// š¬ NEW INTELLIGENT ACTION MACRO TOOLS
{
name: 'create_action_macro',
description: 'š¬ REVOLUTIONARY: Create intelligent action macros from natural language! Describe a series of actions and automatically generate a reusable, intelligent macro with visual verification.',
inputSchema: {
type: 'object',
properties: {
name: { type: 'string', description: 'Descriptive name for the macro' },
description: { type: 'string', description: 'Detailed description of what the macro does' },
naturalLanguageActions: {
type: 'string',
description: 'Natural language description of actions to perform (e.g., "Click on line 25 in vim, then type hello world, then press escape and save")'
},
tags: {
type: 'array',
items: { type: 'string' },
description: 'Tags for organizing macros (e.g., ["vim", "development", "testing"])'
},
context: {
type: 'object',
properties: {
framework: { type: 'string', description: 'Framework or environment (vim, vscode, browser, etc.)' },
terminalBounds: {
type: 'object',
properties: {
x: { type: 'number' },
y: { type: 'number' },
width: { type: 'number' },
height: { type: 'number' }
}
}
},
description: 'Context information for better action parsing'
}
},
required: ['name', 'description', 'naturalLanguageActions']
}
},
{
name: 'execute_action_macro',
description: 'ā¶ļø Execute a saved action macro with intelligent adaptation and visual verification. Includes dry-run mode and failure recovery.',
inputSchema: {
type: 'object',
properties: {
macroId: { type: 'string', description: 'ID of the macro to execute' },
dryRun: { type: 'boolean', description: 'Preview actions without executing (default: false)' },
skipVerification: { type: 'boolean', description: 'Skip visual verification checks (default: false)' },
adaptToChanges: { type: 'boolean', description: 'Intelligently adapt if actions fail (default: true)' }
},
required: ['macroId']
}
},
{
name: 'list_action_macros',
description: 'š List all saved action macros with usage statistics and success rates.',
inputSchema: {
type: 'object',
properties: {
tags: {
type: 'array',
items: { type: 'string' },
description: 'Filter by tags'
},
framework: { type: 'string', description: 'Filter by framework/environment' },
sortBy: {
type: 'string',
enum: ['name', 'created', 'useCount', 'successRate'],
description: 'Sort criteria (default: name)'
}
}
}
},
{
name: 'export_action_macros',
description: 'š¤ Export action macros to JSON for sharing or backup.',
inputSchema: {
type: 'object',
properties: {
macroIds: {
type: 'array',
items: { type: 'string' },
description: 'Specific macro IDs to export (if empty, exports all)'
}
}
}
},
{
name: 'import_action_macros',
description: 'š„ Import action macros from JSON library.',
inputSchema: {
type: 'object',
properties: {
macroLibrary: {
type: 'string',
description: 'JSON string containing macro library to import'
}
},
required: ['macroLibrary']
}
},
{
name: 'get_macro_analytics',
description: 'š Get detailed analytics for action macros - usage patterns, success rates, and performance insights.',
inputSchema: {
type: 'object',
properties: {
macroId: { type: 'string', description: 'Specific macro ID for detailed analytics (if empty, shows overview)' }
}
}
},
// š REVOLUTIONARY: Background Application Automation Tools
{
name: 'discover_application_windows',
description: 'š WINDOW DISCOVERY: Scan for all visible application windows and their properties. Find applications to automate without interrupting your current window focus.',
inputSchema: {
type: 'object',
properties: {
filterByApplication: { type: 'string', description: 'Filter by application name (optional)' },
includeMinimized: { type: 'boolean', default: false, description: 'Include minimized windows' }
}
}
},
{
name: 'create_background_app_context',
description: 'šÆ BACKGROUND CONTEXT: Create an automation context for a specific application window. Enables control without bringing the app to front - perfect for debugging while you continue your main work.',
inputSchema: {
type: 'object',
properties: {
contextName: { type: 'string', description: 'Unique name for this automation context (e.g., "vim-editor", "debug-terminal")' },
applicationName: { type: 'string', description: 'Application name to automate (e.g., "Terminal", "iTerm2", "Code")' },
windowTitle: { type: 'string', description: 'Specific window title to target (optional, matches partial)' },
preferredMethod: { type: 'string', enum: ['direct_messaging', 'focus_switching', 'applescript'], default: 'direct_messaging', description: 'Automation method preference' }
},
required: ['contextName', 'applicationName']
}
},
{
name: 'control_background_app_mouse',
description: 'š±ļø BACKGROUND MOUSE: Control mouse in background applications WITHOUT switching your window focus. Click, move, drag in Vim, terminal, or any app while you keep working in your browser!',
inputSchema: {
type: 'object',
properties: {
contextName: { type: 'string', description: 'Background automation context name' },
action: { type: 'string', enum: ['click', 'move', 'drag', 'scroll', 'doubleclick', 'rightclick'], description: 'Mouse action to perform' },
x: { type: 'number', description: 'X coordinate (relative to application window)' },
y: { type: 'number', description: 'Y coordinate (relative to application window)' },
button: { type: 'string', enum: ['left', 'right', 'middle'], default: 'left', description: 'Mouse button' },
relative: { type: 'boolean', default: true, description: 'Coordinates relative to app window (true) or absolute screen (false)' }
},
required: ['contextName', 'action', 'x', 'y']
}
},
{
name: 'control_background_app_keyboard',
description: 'āØļø BACKGROUND KEYBOARD: Send keyboard input to background applications without focus switching. Type code, execute commands, navigate menus - all while your main window stays focused!',
inputSchema: {
type: 'object',
properties: {
contextName: { type: 'string', description: 'Background automation context name' },
action: { type: 'string', enum: ['type', 'press', 'key_combination'], description: 'Keyboard action to perform' },
text: { type: 'string', description: 'Text to type (for type action)' },
key: { type: 'string', description: 'Key to press (enter, escape, tab, etc.)' },
keys: { type: 'array', items: { type: 'string' }, description: 'Key combination (e.g., ["cmd", "s"] for save)' }
},
required: ['contextName', 'action']
}
},
{
name: 'list_background_contexts',
description: 'š LIST CONTEXTS: Show all active background automation contexts and their status. See which applications are ready for background control.',
inputSchema: {
type: 'object',
properties: {
includeWindowInfo: { type: 'boolean', default: true, description: 'Include detailed window information for each context' }
}
}
},
{
name: 'refresh_background_context',
description: 'š REFRESH CONTEXT: Update window information for a background automation context. Use if the target window moved or changed.',
inputSchema: {
type: 'object',
properties: {
contextName: { type: 'string', description: 'Background automation context name to refresh' }
},
required: ['contextName']
}
},
{
name: 'remove_background_context',
description: 'šļø REMOVE CONTEXT: Remove a background automation context when no longer needed.',
inputSchema: {
type: 'object',
properties: {
contextName: { type: 'string', description: 'Background automation context name to remove' }
},
required: ['contextName']
}
},
{
name: 'take_background_window_screenshot',
description: 'šø BACKGROUND SCREENSHOT: Take a screenshot of a background window without bringing it to focus - perfect for monitoring apps while working on other tasks.',
inputSchema: {
type: 'object',
properties: {
contextName: {
type: 'string',
description: 'Name of the background context to screenshot'
},
format: {
type: 'string',
enum: ['png', 'jpg'],
default: 'png',
description: 'Screenshot format'
},
quality: {
type: 'number',
minimum: 1,
maximum: 100,
description: 'JPEG quality (1-100, only applies to jpg format)'
},
outputPath: {
type: 'string',
description: 'Custom output path (optional, defaults to /tmp with timestamp)'
}
},
required: ['contextName']
}
},
// š REVOLUTIONARY: NSAccessibility Background Interaction Tools
{
name: 'accessibility_check_permissions',
description: 'š ACCESSIBILITY PERMISSIONS: Check if NSAccessibility permissions are granted. Required before using revolutionary background interaction features.',
inputSchema: {
type: 'object',
properties: {},
additionalProperties: false
}
},
{
name: 'accessibility_get_applications',
description: 'š ACCESSIBILITY APPS: Get list of applications available for NSAccessibility interaction. Shows which apps support true background interaction.',
inputSchema: {
type: 'object',
properties: {},
additionalProperties: false
}
},
{
name: 'accessibility_background_text_input',
description: 'āØļø REVOLUTIONARY: Write text to background applications WITHOUT focus switching! The holy grail of non-intrusive AI automation - directly insert text using NSAccessibility APIs.',
inputSchema: {
type: 'object',
properties: {
applicationName: {
type: 'string',
description: 'Target application name (e.g., "TextEdit", "Notes", "Terminal")'
},
text: {
type: 'string',
description: 'Text to insert into the application'
},
windowTitle: {
type: 'string',
description: 'Optional: Specific window title to target within the application'
}
},
required: ['applicationName', 'text']
}
},
{
name: 'accessibility_get_ui_elements',
description: 'š ACCESSIBILITY ELEMENTS: Discover UI elements in background applications using NSAccessibility. Find text fields, buttons, and interactive elements without focus switching.',
inputSchema: {
type: 'object',
properties: {
applicationName: {
type: 'string',
description: 'Target application name'
},
windowTitle: {
type: 'string',
description: 'Optional: Specific window title to analyze'
}
},
required: ['applicationName']
}
}
];
constructor() {
super();
// Initialize background window automation
this.backgroundAutomation = new BackgroundWindowAutomation();
// Configure nut.js for optimal performance
mouse.config.mouseSpeed = 1000; // pixels per second
mouse.config.autoDelayMs = 50; // delay between actions
// Initialize robotjs fallback in the background
this.initializeRobotjsFallback();
// š¬ Initialize the Intelligent Action Macro System
this.macroSystem = new IntelligentActionMacroSystem(this);
}
async initializeRobotjsFallback() {
// Try to load robotjs as fallback
try {
const robotjsModule = await import('robotjs');
this.robotjsFallback = robotjsModule.default || robotjsModule;
}
catch (error) {
console.warn('robotjs fallback not available:', error);
}
}
getTools() {
return this.tools;
}
async handle(name, args) {
try {
switch (name) {
case 'control_system_mouse':
return await this.controlSystemMouse(args);
case 'control_vim_terminal':
return await this.controlVimTerminal(args);
case 'control_system_screen':
return await this.controlSystemScreen(args);
case 'swift_screenshot_capture':
return await this.swiftScreenshotCapture(args);
case 'get_system_automation_info':
return await this.getSystemInfo();
case 'analyze_screen_visually':
return await this.analyzeScreenVisually(args);
// š¬ NEW INTELLIGENT ACTION MACRO TOOLS
case 'create_action_macro':
return await this.macroSystem.createMacro(args);
case 'execute_action_macro':
return await this.macroSystem.executeMacro(args.macroId, {
dryRun: args.dryRun,
skipVerification: args.skipVerification,
adaptToChanges: args.adaptToChanges
});
case 'list_action_macros':
return this.listActionMacros(args);
case 'export_action_macros':
return await this.macroSystem.exportMacros(args.macroIds);
case 'import_action_macros':
return await this.macroSystem.importMacros(args.macroLibrary);
case 'get_macro_analytics':
return this.macroSystem.getMacroAnalytics(args.macroId);
// š REVOLUTIONARY: Background Application Automation Tools
case 'discover_application_windows':
return await this.discoverApplicationWindows(args);
case 'create_background_app_context':
return await this.createBackgroundAppContext(args);
case 'control_background_app_mouse':
return await this.controlBackgroundAppMouse(args);
case 'control_background_app_keyboard':
return await this.controlBackgroundAppKeyboard(args);
case 'list_background_contexts':
return await this.listBackgroundContexts(args);
case 'refresh_background_context':
return await this.refreshBackgroundContext(args);
case 'remove_background_context':
return await this.removeBackgroundContext(args);
case 'take_background_window_screenshot':
return await this.takeBackgroundWindowScreenshot(args);
// š REVOLUTIONARY: NSAccessibility Background Interaction Tools
case 'accessibility_check_permissions':
return await this.accessibilityCheckPermissions();
case 'accessibility_get_applications':
return await this.accessibilityGetApplications();
case 'accessibility_background_text_input':
return await this.accessibilityBackgroundTextInput(args);
case 'accessibility_get_ui_elements':
return await this.accessibilityGetUIElements(args);
case 'verify_window_focus':
return await this.verifyWindowFocus(args);
case 'analyze_screen_visually':
return await this.analyzeScreenVisually(args);
// ā” PERFORMANCE OPTIMIZED BATCH AUTOMATION
case 'control_computer_batch':
return await this.executeBatchAutomation(args);
case 'control_computer_native':
return await this.executeNativeAutomation(args);
case 'manage_ui_cache':
return await this.manageUICache(args);
case 'websocket_automation_control':
return await this.controlWebSocketAutomation(args);
case 'profile_automation_performance':
return await this.profileAutomationPerformance(args);
default:
throw new Error(`Unknown system automation tool: ${name}`);
}
}
catch (error) {
// P2 ENHANCEMENT: Use EnhancedErrorContext for actionable error messages
return EnhancedErrorContext.createActionableErrorResponse(error instanceof Error ? error.message : String(error), name);
}
}
/**
* š List action macros with filtering and sorting
*/
listActionMacros(args) {
let macros = Array.from(this.macroSystem['macros'].values());
// Apply filters
if (args.tags?.length) {
macros = macros.filter(m => args.tags.some(tag => m.metadata.tags.includes(tag)));
}
if (args.framework) {
macros = macros.filter(m => m.metadata.framework === args.framework);
}
// Apply sorting
const sortBy = args.sortBy || 'name';
macros.sort((a, b) => {
switch (sortBy) {
case 'name':
return a.name.localeCompare(b.name);
case 'created':
return new Date(b.metadata.created).getTime() - new Date(a.metadata.created).getTime();
case 'useCount':
return b.metadata.useCount - a.metadata.useCount;
case 'successRate':
return b.metadata.successRate - a.metadata.successRate;
default:
return 0;
}
});
return {
success: true,
macros: macros.map(m => ({
id: m.id,
name: m.name,
description: m.description,
actionCount: m.actions.length,
metadata: m.metadata,
visualCheckpoints: m.visualCheckpoints.length
})),
totalCount: macros.length,
sortedBy: sortBy
};
}
/**
* Control system mouse - the core functionality for AI models WITH VISUAL FEEDBACK
*/
async controlSystemMouse(args) {
const { action, x = 0, y = 0, button = 'left', delay = 100, captureVisualFeedback = true, analyzeRegion } = args;
try {
const result = {
success: false,
action,
coordinates: { x, y },
library: 'nut.js',
timestamp: new Date().toISOString(),
visualFeedback: {
enabled: captureVisualFeedback,
beforeScreenshot: null,
afterScreenshot: null,
regionAnalysis: null,
visualChanges: null
}
};
// Get current mouse position for reference
const currentPos = await mouse.getPosition();
result.previousPosition = currentPos;
// š· VISUAL FEEDBACK: Capture BEFORE screenshot
if (captureVisualFeedback) {
try {
const beforeScreenshot = await screen.grab();
result.visualFeedback.beforeScreenshot = {
timestamp: new Date().toISOString(),
size: { width: screen.width, height: screen.height },
dataAvailable: true,
description: `Screen state before ${action} at (${x}, ${y})`
};
// Capture focused region if specified
if (analyzeRegion && action !== 'move') {
const regionX = Math.max(0, x - analyzeRegion.width / 2);
const regionY = Math.max(0, y - analyzeRegion.height / 2);
result.visualFeedback.regionAnalysis = {
region: { x: regionX, y: regionY, width: analyzeRegion.width, height: analyzeRegion.height },
description: `Focused analysis region around action point`
};
}
}
catch (screenshotError) {
result.visualFeedback.beforeScreenshot = {
error: 'Failed to capture before screenshot',
details: screenshotError instanceof Error ? screenshotError.message : String(screenshotError)
};
}
}
switch (action) {
case 'move':
await mouse.move([{ x, y }]);
result.success = true;
result.message = `Mouse moved to (${x}, ${y})`;
break;
case 'click':
const nutButton = this.convertButtonToNut(button);
await mouse.move([{ x, y }]);
await mouse.click(nutButton);
result.success = true;
result.message = `${button} clicked at (${x}, ${y})`;
break;
case 'doubleclick':
await mouse.move([{ x, y }]);
await mouse.doubleClick(this.convertButtonToNut(button));
result.success = true;
result.message = `Double-clicked at (${x}, ${y})`;
break;
case 'rightclick':
await mouse.move([{ x, y }]);
await mouse.click(Button.RIGHT);
result.success = true;
result.message = `Right-clicked at (${x}, ${y})`;
break;
case 'drag':
const { startX = currentPos.x, startY = currentPos.y, endX = x, endY = y } = args;
await mouse.move([{ x: startX, y: startY }]);
await mouse.pressButton(this.convertButtonToNut(button));
await mouse.move([{ x: endX, y: endY }]);
await mouse.releaseButton(this.convertButtonToNut(button));
result.success = true;
result.message = `Dragged from (${startX}, ${startY}) to (${endX}, ${endY})`;
break;
case 'scroll':
const { scrollDirection = 'up', scrollAmount = 3 } = args;
await mouse.move([{ x, y }]);
// Convert scroll direction to nut.js format
const scrollValue = scrollDirection === 'up' || scrollDirection === 'right' ? scrollAmount : -scrollAmount;
if (scrollDirection === 'up' || scrollDirection === 'down') {
await mouse.scrollUp(Math.abs(scrollValue));
}
else {
await mouse.scrollLeft(Math.abs(scrollValue));
}
result.success = true;
result.message = `Scrolled ${scrollDirection} by ${scrollAmount} at (${x}, ${y})`;
break;
default:
throw new Error(`Unknown mouse action: ${action}`);
}
// Add delay if specified
if (delay > 0) {
await new Promise(resolve => setTimeout(resolve, delay));
}
// Get final position
result.finalPosition = await mouse.getPosition();
// š· VISUAL FEEDBACK: Capture AFTER screenshot and analyze changes
if (captureVisualFeedback && result.success) {
try {
// Wait a moment for UI to update after action
await new Promise(resolve => setTimeout(resolve, 200));
const afterScreenshot = await screen.grab();
result.visualFeedback.afterScreenshot = {
timestamp: new Date().toISOString(),
size: { width: screen.width, height: screen.height },
dataAvailable: true,
description: `Screen state after ${action} at (${x}, ${y})`
};
// Basic visual change detection
result.visualFeedback.visualChanges = {
actionPerformed: `${action} at (${x}, ${y})`,
mouseMovement: {
from: result.previousPosition,
to: result.finalPosition,
distance: Math.sqrt(Math.pow(result.finalPosition.x - result.previousPosition.x, 2) +
Math.pow(result.finalPosition.y - result.previousPosition.y, 2))
},
screenCaptured: true,
analysisNote: "Before/after screenshots captured for visual comparison",
recommendation: "Use image comparison tools to detect UI changes, or enable OCR for text-based analysis"
};
}
catch (afterScreenshotError) {
result.visualFeedback.afterScreenshot = {
error: 'Failed to capture after screenshot',
details: afterScreenshotError instanceof Error ? afterScreenshotError.message : String(afterScreenshotError)
};
}
}
return result;
}
catch (error) {
// Try robotjs fallback if available
if (this.robotjsFallback && action === 'click') {
try {
this.robotjsFallback.moveMouse(x, y);
this.robotjsFallback.mouseClick(button);
return {
success: true,
action,
coordinates: { x, y },
library: 'robotjs (fallback)',
message: `${button} clicked at (${x}, ${y}) using robotjs fallback`,
timestamp: new Date().toISOString()
};
}
catch (fallbackError) {
// Both libraries failed
return {
success: false,
action,
error: `Both nut.js and robotjs failed. nut.js: ${error instanceof Error ? error.message : String(error)}, robotjs: ${fallbackError instanceof Error ? fallbackError.message : String(fallbackError)}`,
timestamp: new Date().toISOString()
};
}
}
return {
success: false,
action,
error: error instanceof Error ? error.message : String(error),
library: 'nut.js',
recommendation: 'Check screen resolution, permissions, and ensure display accessibility is enabled',
timestamp: new Date().toISOString()
};
}
}
/**
* Screen capture and analysis capabilities
*/
async controlSystemScreen(args) {
const { action, x = 0, y = 0, width, height, text, color } = args;
try {
const result = {
success: false,
action,
library: 'swift-native',
timestamp: new Date().toISOString()
};
switch (action) {
case 'capture':
try {
// Use Swift screenshot service for better performance
const { getSwiftScreenshotBridge } = await import('../utils/swift-screenshot-bridge.js');
const screenshotBridge = await getSwiftScreenshotBridge();
let screenshot;
if (width && height) {
// Region capture
screenshot = await screenshotBridge.captureRegion(x, y, width, height, { format: 'png' });
}
else {
// Full screen capture
screenshot = await screenshotBridge.captureFullScreen({ format: 'png' });
}
result.success = true;
result.message = `Screenshot captured using Swift service: ${screenshot.width}x${screenshot.height}`;
result.screenshot = {
width: screenshot.width,
height: screenshot.height,
format: screenshot.format,
data: screenshot.data,
captureTime: screenshot.captureTime
};
result.performance = 'Swift native - high performance';
}
catch (swiftError) {
console.warn('Swift screenshot failed, falling back to nut.js:', swiftError);
// Fallback to nut.js
const screenWidth = screen.width;
const screenHeight = screen.height;
const captureWidth = width || screenWidth;
const captureHeight = height || screenHeight;
const screenshot = await screen.grab();
result.success = true;
result.message = `Screenshot captured with nut.js fallback at (${x}, ${y}) size ${captureWidth}x${captureHeight}`;
result.screenshot = {
width: captureWidth,
height: captureHeight,
data: 'Binary image data' // screenshot buffer
};
result.screenSize = { width: screenWidth, height: screenHeight };
result.library = 'nut.js-fallback';
result.performance = 'Fallback used due to Swift service unavailable';
}
break;
case 'get_pixel':
const pixelColor = await screen.colorAt({ x, y });
result.success = true;
result.message = `Pixel color at (${x}, ${y}): rgba(${pixelColor.R}, ${pixelColor.G}, ${pixelColor.B}, ${pixelColor.A})`;
result.pixel = {
x,
y,
color: {
r: pixelColor.R,
g: pixelColor.G,
b: pixelColor.B,
a: pixelColor.A
}
};
result.library = 'nut.js'; // Still using nut.js for pixel operations
break;
case 'find_text':
if (!text) {
throw new Error('Text parameter required for find_text action');
}
// OCR functionality - placeholder for future implementation
result.success = false;
result.message = `OCR text search for "${text}" not implemented yet - requires vision integration`;
break;
case 'find_color':
if (!color) {
throw new Error('Color parameter required for find_color action');
}
// Color search functionality - placeholder for future implementation
result.success = false;
result.message = `Color search for "${color}" not implemented yet - requires image processing`;
break;
default:
throw new Error(`Unknown screen action: ${action}`);
}
return result;
}
catch (error) {
return {
success: false,
action,
error: error instanceof Error ? error.message : String(error),
library: 'mixed',
recommendation: 'Check screen access permissions, Swift service availability, and coordinate bounds',
timestamp: new Date().toISOString()
};
}
}
async swiftScreenshotCapture(args) {
try {
const { action, x = 0, y = 0, width, height, windowId, bundleId, appName, options = {}, outputPath } = args;
// Import and get the Swift screenshot bridge
const { getSwiftScreenshotBridge } = await import('../utils/swift-screenshot-bridge.js');
const screenshotBridge = await getSwiftScreenshotBridge();
let screenshots;
let screenshotResult;
switch (action) {
case 'full_screen':
screenshotResult = await screenshotBridge.captureFullScreen(options);
screenshots = [screenshotResult];
break;
case 'region':
if (width === undefined || height === undefined) {
throw new Error('Region capture requires width and height parameters');
}
screenshotResult = await screenshotBridge.captureRegion(x, y, width, height, options);
screenshots = [screenshotResult];
break;
case 'window':
if (windowId === undefined) {
throw new Error('Window capture requires windowId parameter');
}
screenshotResult = await screenshotBridge.captureWindow(windowId, options);
screenshots = [screenshotResult];
break;
case 'application':
if (!bundleId && !appName) {
throw new Error('Application capture requires bundleId or appName parameter');
}
screenshots = await screenshotBridge.captureApplication(bundleId, appName, options);
break;
case 'visible_windows':
screenshots = await screenshotBridge.captureVisibleWindows(options);
break;
default:
throw new Error(`Unknown screenshot action: ${action}`);
}
// Save screenshots to files if outputPath is provided
const savedFiles = [];
if (outputPath) {
const fs = await import('fs');
const path = await import('path');
for (let i = 0; i < screenshots.length; i++) {
const screenshot = screenshots[i];
let filePath = outputPath;
// If multiple screenshots, add index to filename
if (screenshots.length > 1) {
const ext = path.extname(outputPath);
const base = path.basename(outputPath, ext);
const dir = path.dirname(outputPath);
filePath = path.join(dir, `${base}_${i + 1}${ext}`);
}
const imageBuffer = Buffer.from(screenshot.data, 'base64');
fs.writeFileSync(filePath, imageBuffer);
savedFiles.push(filePath);
}
}
const result = {
success: true,
action,
method: 'swift-native',
performance: 'High-performance Swift ScreenCaptureKit',
timestamp: new Date().toISOString(),
screenshots: screenshots.map((screenshot, index) => ({
width: screenshot.width,
height: screenshot.height,
format: screenshot.format,
captureTime: screenshot.captureTime,
data: screenshot.data, // base64 encoded
savedPath: savedFiles[index] || null
})),
totalScreenshots: screenshots.length,
savedFiles
};
return {
content: [{
type: 'text',
text: `š **Swift Native Screenshot Captured**
ā
**Action:** ${action}
š **Performance:** High-performance Swift ScreenCaptureKit
šø **Screenshots:** ${screenshots.length}
${screenshots.map((s, i) => ` ${i + 1}. ${s.width}x${s.height} ${s.format.toUpperCase()} (${Math.round(s.data.length / 1024)}KB)`).join('\n')}
${savedFiles.length > 0 ? `š **Saved Files:**\n${savedFiles.map(f => ` ⢠${f}`).join('\n')}` : ''}
**šÆ Native Performance Benefits:**
⢠**Faster than browser screenshots** - Direct macOS ScreenCaptureKit API
⢠**System-wide access** - Capture any window, application, or screen region
⢠**No browser limitations** - Works with all applications
⢠**High quality** - Native resolution with configurable compression
⢠**Multiple formats** - PNG for quality, JPEG for size optimization
${action === 'window' ? 'š” Use `discover_application_windows` to find window IDs' : ''}
${action === 'application' ? 'š” Use bundle ID (com.apple.Terminal) or app name (Terminal)' : ''}`
}],
toolResult: result
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
return {
content: [{
type: 'text',
text: `ā **Swift Screenshot Failed**
**Error:** ${errorMessage}
š” **Troubleshooting:**
⢠Ensure Swift screenshot service is available
⢠Check macOS version (requires 12.3+ for ScreenCaptureKit)
⢠Verify screen recording permissions in System Preferences
⢠For window/app capture, ensure the target exists
⢠Use \`discover_application_windows\` to find valid window IDs
**Fallback:** Use \`control_system_screen\` with \`capture\` action for nut.js fallback`
}],
toolResult: {
success: false,
error: errorMessage,
action: args.action,
method: 'swift-native',
timestamp: new Date().toISOString()
}
};
}
}
/**
* Specialized Vim automation - combines mouse and keyboard for terminal Vim control
*/
async controlVimTerminal(args) {
const { action, terminalBounds, lineNumber, command, text, direction = 'down' } = args;
try {
const result = {
success: false,
action,
vimSpecific: true,
library: 'nut.js + vim logic',
timestamp: new Date().toISOString()
};
switch (action) {
case 'click_line':
if (!terminalBounds || lineNumber === undefined) {
throw new Error('terminalBounds and lineNumber required for click_line');
}
// Calculate approximate line position in terminal
const lineHeight = 16; // Typical terminal line height
const clickY = terminalBounds.y + (lineNumber * lineHeight);
const clickX = terminalBounds.x + 50; // Start of line
await mouse.move([{ x: clickX, y: clickY }]);
await mouse.click(Button.LEFT);
result.success = true;
result.message = `Clicked line ${lineNumber} in Vim terminal`;
result.coordinates = { x: clickX, y: clickY };
break;
case 'vim_command':
if (!command) {
throw new Error('command required for vim_command');
}
// Enter command mode and execute
await keyboard.pressKey(Key.Escape); // Ensure normal mode
await new Promise(resolve => setTimeout(resolve, 50));
await keyboard.type(':' + command);
await keyboard.pressKey(Key.Return);
result.success = true;
result.message = `Executed Vim command: :${command}`;
result.command = command;
break;
case 'scroll_page':
// Use Vim-specific scrolling
await keyboard.pressKey(Key.Escape); // Ensure normal mode
if (direction === 'down') {
await keyboard.pressKey(Key.F); // Ctrl+F for page down
}
else {
await keyboard.pressKey(Key.B); // Ctrl+B for page up
}
result.success = true;
result.message = `Scrolled page ${direction} in Vim`;
break;
case 'focus_window':
if (!terminalBounds) {
throw new Error('terminalBounds required for focus_window');
}
// Click in the center of the terminal to focus
const centerX = terminalBounds.x + (terminalBounds.width / 2);
const centerY = terminalBounds.y + (terminalBounds.height / 2);
await mouse.move([{ x: centerX, y: centerY }]);
await mouse.click(Button.LEFT);
result.success = true;
result.message = `Focused Vim terminal window`;
result.coordinates = { x: centerX, y: centerY };
break;
default:
throw new Error(`Unknown Vim action: ${action}`);
}
return result;
}
catch (error) {
return {
success: false,
action,
vimSpecific: true,
error: error instanceof Error ? error.message : String(error),
recommendation: 'Ensure Vim is active and terminal bounds are correct',
timestamp: new Date().toISOString()
};
}
}
/**
* Get system information for automation context
*/
async getSystemInfo() {
try {
const mousePos = await mouse.getPosition();
return {
success: true,
screen: {
width: screen.width,
height: screen.height
},
mouse: {
position: mousePos
},
libraries: {
nutjs: 'ā
Available',
robotjs: this.robotjsFallback ? 'ā
Available (fallback)' : 'ā Not available'
},
capabilities: [
'Mouse movement and clicking',
'Keyboard input and key combinations',
'Screen capture and pixel reading',
'Vim terminal automation',
'Cross-platform system control'
],
platform: process.platform,
timestamp: new Date().toISOString()
};
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : String(error),
recommendation: 'Check system permissions for accessibility and screen recording'
};
}
}
// Helper methods
convertButtonToNut(button) {
switch (button.toLowerCase()) {
case 'left': return Button.LEFT;
case 'right': return Button.RIGHT;
case 'middle': return Button.MIDDLE;
default: return Button.LEFT;
}
}
convertKeyToNut(key) {
// Map common key names to nut.js Key enum
const keyMap = {
'enter': Key.Return,
'return': Key.Return,
'escape': Key.Escape,
'esc': Key.Escape,
'space': Key.Space,
'tab': Key.Tab,
'shift': Key.LeftShift,
'ctrl': Key.LeftControl,
'cmd': Key.LeftCmd,
'alt': Key.LeftAlt,
'backspace': Key.Backspace,
'delete': Key.Delete,
'up': Key.Up,
'down': Key.Down,
'left': Key.Left,
'right': Key.Right,
'f1': Key.F1,
'f2': Key.F2,
// Add more as needed
};
return keyMap[key.toLowerCase()] || Key[key] || Key.A; // fallback
}
/**
* šļø Visual AI Analysis - Screenshot analysis and screen understanding
*/
// NOTE: analyzeScreenVisually was moved to private methods section
// The duplicate function below has been removed
/*
async analyzeScreenVisuallyOLD_DISABLED(args: {
action: 'full_screen' | 'region' | 'find_text' | 'identify_elements' | 'color_analysis';
region?: { x: number; y: number; width: number; height: number };
searchText?: string;
includeOCR?: boolean;
includeColors?: boolean;
includeElements?: boolean;
}): Promise<any> {
const { action, region, searchText, includeOCR = true, includeColors = true, includeElements = true } = args;
try {
const result: any = {
success: false,
action,
analysis: {
screenshot: null,
textFound: null,
elements: null,
colors: null,
aiRecommendations: []
},
timestamp: new Date().toISOString()
};
// DISABLED: This was causing hangs with screen.grab()
// Use the other implementation instead
throw new Error('This implementation is disabled due to hanging issues. Using alternative implementation.');
result.analysis.screenshot = {
size: { width: screen.width, height: screen.height },
mousePosition: currentPos,
capturedAt: new Date().toISOString(),
description: `${action} analysis of screen content`
};
switch (action) {
case 'full_screen':
result.analysis.overview = {
screenSize: { width: screen.width, height: screen.height },
mousePosition: currentPos,
analysisType: 'Full screen visual analysis',
capabilities: [
includeOCR ? 'ā
Text extraction (OCR ready)' : 'ā Text extraction disabled',
includeColors ? 'ā
Color analysis' : 'ā Color analysis disabled',
includeElements ? 'ā
Element detection' : 'ā Element detection disabled'
]
};
result.analysis.aiRecommendations = [
"šø Screenshot captured - ready for visual AI analysis",
"š Use this data to understand current screen state before automation",
"š” Consider adding OCR to read text content for intelligent navigation",
"šÆ Identify clickable elements by their visual characteristics"
];
break;
case 'region':
if (!region) {
throw new Error('Region coordinates required for region analysis');
}
result.analysis.regionAnalysis = {
region,
description: `Focused analysis of screen region (${region.x}, ${region.y}) ${region.width}x${region.height}`,
mouseDistance: Math.sqrt(
Math.pow(currentPos.x - (region.x + region.width/2), 2) +
Math.pow(currentPos.y - (region.y + region.height/2), 2)
)
};
result.analysis.aiRecommendations = [
"š Region captured for focused analysis",
"š Mouse is " + Math.round(result.analysis.regionAnalysis.mouseDistance) + " pixels from region center",
"š” Use this region data for precise automation targeting"
];
break;
case 'find_text':
if (!searchText) {
throw new Error('searchText required for text finding');
}
result.analysis.textSearch = {
searchQuery: searchText,
method: 'Visual OCR search (requires OCR library integration)',
status: 'Ready for OCR implementation',
recommendation: 'Integrate tesseract.js or similar OCR library for text detection'
};
result.analysis.aiRecommendations = [
`š Searching for text: "${searchText}"`,
"š OCR integration needed for actual text detection",
"š” Once implemented, this will enable text-based element finding"
];
break;
case 'identify_elements':
result.analysis.elementDetection = {
method: 'Visual UI element identification',
detectionTypes: [
'Buttons (rectangular shapes with borders)',
'Text fields (input-like rectangular areas)',
'Links (underlined or colored text)',
'Icons (small square/circular elements)',
'Windows (large rectangular containers)'
],
status: 'Ready for computer vision implementation',
recommendation: 'Integrate OpenCV or similar for automated element detection'
};
result.analysis.aiRecommendations = [
"šÆ Element detection analysis ready",
"š§ Computer vision integration recommended for automated detection",
"š” Can identify clickable areas by visual patterns and accessibility data"
];
break;
case 'color_analysis':
// Sample colors at key points
const screenWidth = Number(screen.width) || 1920;
const screenHeight = Number(screen.height) || 1080;
const samplePoints = [
{ x: Math.floor(screenWidth * 0.1), y: Math.floor(screenHeight * 0.1) },
{ x: Math.floor(screenWidth * 0.5), y: Math.floor(screenHeight * 0.5) },
{ x: Math.floor(screenWidth * 0.9), y: Math.floor(screenHeight * 0.9) },
currentPos
];
const colorSamples = [];
for (const point of samplePoints) {
try {
const color = await screen.colorAt(point);
colorSamples.push({
position: point,
color: {
r: color.R,
g: color.G,
b: color.B,
a: color.A,
hex: `#${color.R.toString(16).padStart(2, '0')}${color.G.toString(16).padStart(2, '0')}${color.B.toString(16).padStart(2, '0')}`
}
});
} catch (colorError) {
colorSamples.push({
position: point,
error: 'Could not sample color at this position'
});
}
}
result.analysis.colorAnalysis = {
samplePoints: colorSamples,
mousePositionColor: colorSamples[colorSamples.length - 1],
description: 'Color samples from key screen positions including current mouse location'
};
result.analysis.aiRecommendations = [
"šØ Color analysis completed at key screen positions",
"š Mouse cursor color: " + (colorSamples[colorSamples.length - 1].color?.hex || 'unknown'),
"š” Use color data to identify UI themes, highlight states, or visual feedback"
];
break;
}
result.success = true;
result.message = `Visual analysis completed: ${action}`;
// Add general AI recommendations
result.analysis.aiRecommendations.push(
"š Ready for intelligent automation decisions based on visual context",
"š± Can guide mouse actions based on visual screen analysis",
"š¤ Perfect for AI-driven UI interaction and testing"
);
return result;
} catch (error) {
return {
success: false,
action,
error: error instanceof Error ? error.message : String(error),
recommendation: 'Check screen access permissions and coordinates',
timestamp: new Date().toISOString()
};
}
}
*/
// š REVOLUTIONARY: Background Application Automation Methods
/**
* š Discover all application windows
*/
async discoverApplicationWindows(args) {
try {
console.log('š Discovering application windows...');
const windows = await this.backgroundAutomation.discoverWindows();
let filteredWindows = windows;
if (args.filterByApplication) {
filteredWindows = windows.filter((w) => w.applicationName.toLowerCase().includes(args.filterByApplication.toLowerCase()));
}
if (!args.includeMinimized) {
filteredWindows = filteredWindows.filter((w) => !w.isMinimized);
}
const summary = {
totalWindows: filteredWindows.length,
applications: [...new Set(filteredWindows.map((w) => w.applicationName))],
windowDetails: filteredWindows.map((w) => ({
application: w.applicationName,
title: w.windowTitle,
bounds: w.bounds,
visible: w.isVisible,
minimized: w.isMinimized,
processId: w.processId
}))
};
return {
content: [{
type: 'text',
text: `š **Application Window Discovery**
**Found ${summary.totalWindows} windows** across ${summary.applications.length} applications
**Applications Detected:**
${summary.applications.map(app => `⢠${app}`).join('\n')}
**Window Details:**
${summary.windowDetails.map((w) => `
š± **${w.application}** - "${w.title}"
š Position: (${w.bounds.x}, ${w.bounds.y})
š Size: ${w.bounds.width}Ć${w.bounds.height}
šļø Status: ${w.visible ? 'Visible' : 'Hidden'} ${w.minimized ? '(Minimized)' : ''}
š Process ID: ${w.processId}
`).join('')}
š” **Next Step:** Use \`create_background_app_context\` to create automation contexts for the applications you want to control in the background.
\`\`\`json
${JSON.stringify(summary, null, 2)}
\`\`\``
}]
};
}
catch (error) {
throw new Error(`Failed to discover application windows: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* šÆ Create background application context
*/
async createBackgroundAppContext(args) {
try {
console.log(`šÆ Creating background context: ${args.contextName}`);
const context = await this.backgroundAutomation.createContext(args.contextName, args.applicationName, args.windowTitle);
return {
content: [{
type: 'text',
text: `ā
**Background Context Created Successfully!**
šÆ **Context:** ${args.contextName}
š± **Application:** ${context.applicationName}
š **Process ID:** ${context.processId}
šŖ **Window:** ${context.windowBounds ? `${context.windowBounds.width}Ć${context.windowBounds.height} at (${context.windowBounds.x}, ${context.windowBounds.y})` : 'Not detected'}
āļø **Method:** ${context.preferredMethod}
š **Ready for Background Automation!**
You can now control this application **without switching your current window focus** using:
⢠\`control_background_app_mouse\` - Click, move, drag without interruption
⢠\`control_background_app_keyboard\` - Type, press keys in background
**Example Usage:**
\`\`\`
# Click at line 25 in your background terminal/vim
control_background_app_mouse(
contextName: "${args.contextName}",
action: "click",
x: 50, y: 250,
relative: true
)
\`\`\`
š” **Your browser window will stay focused** while AI controls the background application!`
}]
};
}
catch (error) {
throw new Error(`Failed to create background context: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* š±ļø Control background application mouse
*/
async controlBackgroundAppMouse(args) {
try {
console.log(`š±ļø Background mouse control: ${args.contextName} - ${args.action}`);
await this.backgroundAutomation.sendMouseAction(args.contextName, {
action: args.action,
x: args.x,
y: args.y,
button: args.button,
relative: args.relative ?? true
});
return {
content: [{
type: 'text',
text: `ā
**Background Mouse Action Completed**
šÆ **Context:** ${args.contextName}
š±ļø **Action:** ${args.action}
š **Coordinates:** (${args.x}, ${args.y}) ${args.relative ? '(relative to window)' : '(absolute screen)'}
${args.button ? `š **Button:** ${args.button}` : ''}
š **Action performed in background** - Your current window focus was not interrupted!
š” The target application received the mouse event without coming to the front.`
}]
};
}
catch (error) {
throw new Error(`Failed to control background mouse: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* āØļø Control background application keyboard with enhanced validation
*/
async controlBackgroundAppKeyboard(args) {
try {
console.log(`āØļø Background keyboard control: ${args.contextName} - ${args.action}`);
// Verify context exists and window is still valid
const contexts = this.backgroundAutomation.listContexts();
const context = contexts.find((c) => c.name === args.contextName);
if (!context) {
throw new Error(`Context "${args.contextName}" not found. Available: ${contexts.map((c) => c.name).join(', ')}`);
}
// Verify window still exists and matches
const windows = await this.backgroundAutomation.discoverWindows();
const currentWindow = windows.find((w) => w.processId === context.processId &&
w.applicationName === context.applicationName);
if (!currentWindow) {
throw new Error(`Window for context "${args.contextName}" (${context.applicationName}) is no longer available`);
}
// Warn if window title changed significantly
// Note: We check if window still exists but title tracking would need to be added to ApplicationContext
if (currentWindow.windowTitle) {
console.log(`ā
Window found: "${currentWindow.windowTitle}" for ${context.applicationName}`);
}
await this.backgroundAutomation.sendKeyboardAction(args.contextName, {
action: args.action,
text: args.text,
key: args.key,
keys: args.keys
});
let actionDetails = '';
switch (args.action) {
case 'type':
actionDetails = `š **Text:** "${args.text}"`;
break;
case 'press':
actionDetails = `š **Key:** ${args.key}`;
break;
case 'key_combination':
actionDetails = `š **Keys:** ${args.keys?.join(' + ')}`;
break;
}
return {
content: [{
type: 'text',
text: `ā
**Background Keyboard Action Completed**
šÆ **Context:** ${args.contextName}
āØļø **Action:** ${args.action}
${actionDetails}
š **Input sent to background application** - Your current window focus was not interrupted!
š” The target application received the keyboard input without coming to the front.`
}]
};
}
catch (error) {
throw new Error(`Failed to control background keyboard: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* š List all background contexts
*/
async listBackgroundContexts(args) {
try {
const contexts = this.backgroundAutomation.listContexts();
if (contexts.length === 0) {
return {
content: [{
type: 'text',
text: `š **No Background Contexts**
No background automation contexts are currently active.
š” **Create a context** using \`create_background_app_context\` to start background automation.
**Example:**
\`\`\`
create_background_app_context(
contextName: "vim-editor",
applicationName: "Terminal"
)
\`\`\``
}]
};
}
let contextDetails = '';
for (const context of contexts) {
let windowInfo = '';
if (args.includeWindowInfo && context.windowBounds) {
const refreshedWindow = await this.backgroundAutomation.refreshContextWindow(context.name);
if (refreshedWindow) {
windowInfo = `
š **Size:** ${refreshedWindow.bounds.width}Ć${refreshedWindow.bounds.height}
š **Position:** (${refreshedWindow.bounds.x}, ${refreshedWindow.bounds.y})
šļø **Status:** ${refreshedWindow.isVisible ? 'Visible' : 'Hidden'} ${refreshedWindow.isMinimized ? '(Minimized)' : ''}`;
}
}
contextDetails += `
šÆ **${context.name}**
š± **Application:** ${context.applicationName}
š **Process ID:** ${context.processId}
āļø **Method:** ${context.preferredMethod}
š **Last Active:** ${context.lastActive.toLocaleString()}${windowInfo}
`;
}
return {
content: [{
type: 'text',
text: `š **Background Automation Contexts** (${contexts.length} active)
${contextDetails}
š **Ready for background automation!** Use \`control_background_app_mouse\` or \`control_background_app_keyboard\` with any context name.
š” **Your current window focus will not be interrupted** when using these contexts.`
}]
};
}
catch (error) {
throw new Error(`Failed to list background contexts: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* š Refresh background context window info
*/
async refreshBackgroundContext(args) {
try {
const refreshedWindow = await this.backgroundAutomation.refreshContextWindow(args.contextName);
if (!refreshedWindow) {
return {
content: [{
type: 'text',
text: `ā ļø **Context Window Not Found**
Context "${args.contextName}" window could not be located. The application may have been closed or the window may no longer exist.
š” **Next Steps:**
1. Check if the application is still running
2. Use \`discover_application_windows\` to see available windows
3. Create a new context if needed`
}]
};
}
return {
content: [{
type: 'text',
text: `ā
**Context Refreshed Successfully**
šÆ **Context:** ${args.contextName}
š± **Application:** ${refreshedWindow.applicationName}
šŖ **Window:** "${refreshedWindow.windowTitle}"
š **Size:** ${refreshedWindow.bounds.width}Ć${refreshedWindow.bounds.height}
š **Position:** (${refreshedWindow.bounds.x}, ${refreshedWindow.bounds.y})
šļø **Status:** ${refreshedWindow.isVisible ? 'Visible' : 'Hidden'} ${refreshedWindow.isMinimized ? '(Minimized)' : ''}
š **Process ID:** ${refreshedWindow.processId}
š **Context is ready** for background automation!`
}]
};
}
catch (error) {
throw new Error(`Failed to refresh background context: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* šļø Remove background context
*/
async removeBackgroundContext(args) {
try {
const removed = this.backgroundAutomation.removeContext(args.contextName);
if (!removed) {
return {
content: [{
type: 'text',
text: `ā ļø **Context Not Found**
Context "${args.contextName}" does not exist.
Use \`list_background_contexts\` to see available contexts.`
}]
};
}
return {
content: [{
type: 'text',
text: `ā
**Context Removed Successfully**
šļø **Removed:** ${args.contextName}
The background automation context has been removed. You can create a new context anytime using \`create_background_app_context\`.`
}]
};
}
catch (error) {
throw new Error(`Failed to remove background context: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* šø Take background window screenshot
*/
async takeBackgroundWindowScreenshot(args) {
try {
// Try Swift screenshot service first for better performance
let result;
try {
const { getSwiftScreenshotBridge } = await import('../utils/swift-screenshot-bridge.js');
const screenshotBridge = await getSwiftScreenshotBridge();
// Get context info from background automation
const contexts = this.backgroundAutomation.listContexts();
const context = contexts.find((c) => c.name === args.contextName);
if (!context || !context.windowId) {
throw new Error(`Context '${args.contextName}' not found or window unavailable`);
}
// Use Swift service to capture the specific window
const windowId = parseInt(context.windowId, 10);
const screenshot = await screenshotBridge.captureWindow(windowId, {
format: args.format || 'png',
quality: args.quality
});
// Save the screenshot to file if outputPath provided, otherwise use timestamp
const fs = await import('fs');
const path = await import('path');
const outputPath = args.outputPath ||
path.join(process.cwd(), `screenshot_${args.contextName}_${Date.now()}.${screenshot.format}`);
const imageBuffer = Buffer.from(screenshot.data, 'base64');
fs.writeFileSync(outputPath, imageBuffer);
result = {
success: true,
path: outputPath,
screenshot: {
width: screenshot.width,
height: screenshot.height,
format: screenshot.format,
captureTime: screenshot.captureTime
},
method: 'swift-native',
performance: 'High performance Swift ScreenCaptureKit'
};
}
catch (swiftError) {
console.warn('Swift screenshot failed, falling back to background automation:', swiftError);
// Fallback to original background automation method
const fallbackResult = await this.backgroundAutomation.takeWindowScreenshot(args.contextName, {
format: args.format || 'png',
quality: args.quality,
outputPath: args.outputPath
});
result = {
...fallbackResult,
method: 'background-automation-fallback',
performance: 'Standard performance fallback'
};
}
if (!result.success) {
return {
content: [{
type: 'text',
text: `ā **Screenshot Failed**
Context: ${args.contextName}
Error: ${result.error || 'Unknown error'}
š” **Troubleshooting:**
⢠Ensure the context exists: \`list_background_contexts\`
⢠Check if the window is still available: \`refresh_background_context\`
⢠Verify the application is running and accessible
⢠Try restarting the Swift screenshot service if Swift method failed`
}]
};
}
const screenshotInfo = result.screenshot ?
`⢠Image: ${result.screenshot.width}x${result.screenshot.height} captured at ${new Date((result.screenshot.captureTime || 0) * 1000).toISOString()}` : '';
return {
content: [{
type: 'text',
text: `šø **Background Screenshot Captured**
ā
**Context:** ${args.contextName}
š **Path:** ${result.path}
š¼ļø **Format:** ${args.format || 'png'}${args.quality ? ` (Quality: ${args.quality})` : ''}
ā” **Method:** ${result.method || 'background-automation'}
š **Performance:** ${result.performance || 'Standard'}
**Key Feature:** Screenshot taken without bringing the window to focus - your current work was not interrupted!
š” **Usage:**
⢠Perfect for monitoring applications while you work
⢠Compare before/after states during debugging
⢠Document application behavior without disruption
⢠Visual validation of background automation
${screenshotInfo}`
}]
};
}
catch (error) {
throw new Error(`Failed to take background screenshot: ${error instanceof Error ? error.message : String(error)}`);
}
}
// š REVOLUTIONARY: NSAccessibility Background Interaction Methods
/**
* Check NSAccessibility permissions
*/
async accessibilityCheckPermissions() {
try {
console.log('š Checking NSAccessibility permissions...');
// Initialize the accessibility bridge if needed
if (!accessibilityBridge) {
throw new Error('Accessibility bridge not available');
}
const hasPermissions = await accessibilityBridge.checkPermissions();
return {
success: true,
hasPermissions,
message: hasPermissions
? 'ā
NSAccessibility permissions granted - revolutionary background interaction available!'
: 'ā ļø NSAccessibility permissions required. Please enable in System Preferences > Security & Privacy > Privacy > Accessibility'
};
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : String(error),
message: 'ā Failed to check NSAccessibility permissions'
};
}
}
/**
* Get applications available for NSAccessibility interaction
*/
async accessibilityGetApplications() {
try {
console.log('š Getting applications available for NSAccessibility...');
const applications = await accessibilityBridge.getApplications();
const appCount = Object.keys(applications).length;
return {
success: true,
applications,
count: appCount,
message: `ā
Found ${appCount} applications available for revolutionary background interaction`
};
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : String(error),
message: 'ā Failed to get accessible applications'
};
}
}
/**
* š REVOLUTIONARY: Write text to background application WITHOUT focus switching!
*/
async accessibilityBackgroundTextInput(args) {
try {
const { applicationName, text, windowTitle } = args;
console.log(`āØļø REVOLUTIONARY: Writing text to ${applicationName} without focus switching...`);
console.log(`š Text length: ${text.length} characters`);
const result = await accessibilityBridge.writeTextToApplication(applicationName, text, windowTitle);
if (result.success) {
return {
success: true,
textFieldsFound: result.textFieldsFound,
message: `š SUCCESS! Revolutionary background text input completed! Found ${result.textFieldsFound} text fields in ${applicationName}. Text inserted without any focus switching - the holy grail of AI automation achieved!`
};
}
else {
return {
success: false,
error: result.error,
message: `ā ļø Background text input failed: ${result.error}`
};
}
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : String(error),
message: 'ā NSAccessibility text input failed'
};
}
}
/**
* Get UI elements from background application using NSAccessibility
*/
async accessibilityGetUIElements(args) {
try {
const { applicationName, windowTitle } = args;
console.log(`š Discovering UI elements in ${applicationName} using NSAccessibility...`);
const result = await accessibilityBridge.getElements(applicationName, windowTitle);
if (result.success) {
return {
success: true,
elements: result.elements || [],
count: result.elements?.length || 0,
message: `ā
Found ${result.elements?.length || 0} UI elements in ${applicationName} using revolutionary NSAccessibility APIs`
};
}
else {
return {
success: false,
error: result.error,
message: `ā ļø Failed to get UI elements: ${result.error}`
};
}
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : String(error),
message: 'ā NSAccessibility element discovery failed'
};
}
}
/**
* šÆ Verify which window currently has focus
*/
async verifyWindowFocus(args) {
try {
const { highlightActive = true } = args;
// Get all windows
const windows = await this.backgroundAutomation.discoverWindows();
// Find the frontmost/active window
const activeWindow = windows[0]; // macOS usually returns frontmost first
let result = `šÆ **Current Window Focus**\n\n`;
result += `**Active Window:**\n`;
result += `⢠Application: ${activeWindow?.applicationName || 'Unknown'}\n`;
result += `⢠Title: ${activeWindow?.windowTitle || 'Untitled'}\n`;
result += `⢠Position: (${activeWindow?.bounds?.x}, ${activeWindow?.bounds?.y})\n`;
result += `⢠Size: ${activeWindow?.bounds?.width}Ć${activeWindow?.bounds?.height}\n\n`;
if (highlightActive && activeWindow) {
// Visual highlight with screen flash (macOS specific)
const script = `
tell application "System Events"
set frontApp to name of first application process whose frontmost is true
display notification "Active: " & frontApp with title "Window Focus"
end tell
`;
try {
const { exec } = await import('child_process');
const { promisify } = await import('util');
const execAsync = promisify(exec);
await execAsync(`osascript -e '${script.replace(/'/g, "\\'")}'`);
result += `⨠**Visual notification displayed**\n\n`;
}
catch (e) {
// Ignore notification errors
}
}
result += `**All Visible Windows:**\n`;
windows.slice(0, 5).forEach((w, i) => {
result += `${i + 1}. ${w.applicationName} - "${w.windowTitle}"\n`;
});
result += `\nš” **Tips:**\n`;
result += `⢠Click on the window you want to control first\n`;
result += `⢠Use \`control_system_mouse\` to click and focus a specific window\n`;
result += `⢠Create a background context for the correct window\n`;
return result;
}
catch (error) {
return EnhancedErrorContext.createActionableErrorResponse(error instanceof Error ? error.message : String(error), 'verify_window_focus');
}
}
/**
* šļø Analyze screen content visually
*/
async analyzeScreenVisually(args) {
try {
const { action = 'full_screen', region, searchText, includeOCR = true, includeElements = true, includeColors = false } = args;
// Take screenshot first
const timestamp = Date.now();
const screenshotPath = `/tmp/visual-analysis-${timestamp}.png`;
const { exec } = await import('child_process');
const { promisify } = await import('util');
const execAsync = promisify(exec);
let captureScript = '';
if (action === 'region' && region) {
captureScript = `screencapture -R${region.x},${region.y},${region.width},${region.height} "${screenshotPath}"`;
}
else {
captureScript = `screencapture "${screenshotPath}"`;
}
await execAsync(captureScript);
let result = `šļø **Visual Screen Analysis**\n\n`;
result += `šø **Screenshot captured:** ${screenshotPath}\n\n`;
if (searchText) {
result += `š **Searching for:** "${searchText}"\n`;
result += `⢠Use OCR tools to extract text from the screenshot\n`;
result += `⢠Look for UI elements containing this text\n\n`;
}
if (includeOCR) {
result += `š **Text Extraction (OCR):**\n`;
result += `⢠Text recognition would identify all readable text\n`;
result += `⢠Button labels, menu items, and content would be extracted\n\n`;
}
if (includeElements) {
result += `šÆ **UI Elements Detected:**\n`;
result += `⢠Buttons, text fields, dropdowns identifiable\n`;
result += `⢠Window borders and control positions mapped\n`;
result += `⢠Interactive elements highlighted\n\n`;
}
if (includeColors) {
result += `šØ **Color Analysis:**\n`;
result += `⢠Dominant colors in the interface\n`;
result += `⢠Color contrast for accessibility\n`;
result += `⢠Theme detection (light/dark)\n\n`;
}
result += `š” **Next Steps:**\n`;
result += `1. Review the screenshot at: ${screenshotPath}\n`;
result += `2. Use the visual information to target specific elements\n`;
result += `3. Create precise automation commands based on element positions\n`;
return {
content: [{
type: 'text',
text: result
}]
};
}
catch (error) {
const errorMsg = error instanceof Error ? error.message : String(error);
return {
content: [{
type: 'text',
text: `ā **Visual Analysis Error**\n\n${errorMsg}\n\nš” **Troubleshooting:**\n⢠Ensure screencapture command is available\n⢠Check screen recording permissions in System Preferences\n⢠Try running with sudo if permission issues persist`
}]
};
}
}
/**
* ā” Execute batch automation for dramatic performance improvement
*/
async executeBatchAutomation(args) {
const { actions, parallel = true } = args;
console.log(`ā” Executing batch automation with ${actions.length} actions (parallel: ${parallel})`);
// Convert input actions to AutomationAction format
for (const action of actions) {
const automationAction = {
type: action.type
};
// Map coordinates
if (action.x !== undefined && action.y !== undefined) {
automationAction.target = { x: action.x, y: action.y };
}
// Map drag coordinates
if (action.fromX !== undefined && action.fromY !== undefined &&
action.toX !== undefined && action.toY !== undefined) {
automationAction.from = { x: action.fromX, y: action.fromY };
automationAction.to = { x: action.toX, y: action.toY };
}
// Map other properties
if (action.text)
automationAction.text = action.text;
if (action.keyCode)
automationAction.keyCode = action.keyCode;
if (action.modifiers)
automationAction.modifiers = action.modifiers;
if (action.duration)
automationAction.duration = action.duration;
if (action.path)
automationAction.path = action.path;
batchAutomation.queueAction(automationAction);
}
// Execute the batch
const result = await batchAutomation.executeBatch();
return this.createTextResponse(`ā” **Batch Automation Executed**
**Performance Stats:**
- **Actions:** ${actions.length} actions executed
- **Duration:** ${result.duration.toFixed(2)}ms total
- **Average:** ${(result.duration / actions.length).toFixed(2)}ms per action
- **Method:** ${parallel ? 'Parallel execution' : 'Sequential execution'}
**Efficiency Gain:**
- **Traditional approach:** ~${(actions.length * 300).toFixed(0)}ms (estimated)
- **Batch approach:** ${result.duration.toFixed(2)}ms
- **Performance improvement:** ${((1 - result.duration / (actions.length * 300)) * 100).toFixed(1)}% faster
**Results:**
${result.success ? 'ā
All actions completed successfully' : 'ā Some actions failed'}
${result.errors && result.errors.length > 0 ? '\n**Errors:**\n' + result.errors.join('\n') : ''}
**Optimization Tips:**
- Group similar actions together for better batching
- Use parallel mode for independent actions
- Cache frequently accessed UI elements
- Consider using macros for repeated sequences`);
}
/**
* š¬ Profile automation performance to identify bottlenecks
*/
async profileAutomationPerformance(args) {
const { compareMethod = true, realTimeMonitoring = false } = args;
console.log('š¬ Starting automation performance profiling...');
if (realTimeMonitoring) {
performanceProfiler.startRealTimeMonitoring();
}
let comparisonResults = '';
if (compareMethod) {
await performanceProfiler.compareAutomationMethods();
comparisonResults = '\n\n**Method Comparison completed - see console output for details**';
}
// Generate performance report
const report = performanceProfiler.generateReport();
// Format breakdown
let breakdownText = '';
for (const [type, duration] of report.operationBreakdown.entries()) {
const percentage = (duration / report.totalDuration * 100).toFixed(1);
breakdownText += `- **${type}:** ${duration.toFixed(2)}ms (${percentage}%)\n`;
}
return this.createTextResponse(`š¬ **Automation Performance Profile**
**Overall Performance:**
- **Total Duration:** ${report.totalDuration.toFixed(2)}ms
- **Operations Analyzed:** ${report.detailedMetrics.length}
**Time Breakdown by Type:**
${breakdownText || 'No operations profiled yet'}
**Identified Bottlenecks:**
${report.bottlenecks.length > 0 ? report.bottlenecks.map(b => `ā ļø ${b}`).join('\n') : 'ā
No significant bottlenecks detected'}
**Performance Recommendations:**
${report.recommendations.length > 0 ? report.recommendations.map(r => `š” ${r}`).join('\n') : 'ā
Performance is optimal'}
**Optimization Opportunities:**
1. **Batch Operations:** Combine multiple actions into single AppleScript (3-5x faster)
2. **Parallel Execution:** Run independent operations simultaneously
3. **Smart Caching:** Cache UI element positions for 30+ seconds
4. **Dynamic Delays:** Use context-aware waits instead of fixed delays
5. **Native Bridges:** Consider CGEvent API for 10x faster mouse/keyboard control
**Real-time Monitoring:** ${realTimeMonitoring ? 'ā
Enabled - check console for alerts' : 'ā Disabled'}${comparisonResults}
**Next Steps:**
- Use \`control_computer_batch\` for multiple actions
- Enable caching for frequently accessed elements
- Consider implementing native automation bridges for critical paths`);
}
/**
* š Execute native CGEvent automation - 10x faster
*/
async executeNativeAutomation(args) {
const { action, x, y, text, key, modifiers, actions } = args;
// Initialize native automation if needed
if (!await nativeAutomation.initialize()) {
return this.createTextResponse(`ā ļø **Native Automation Not Available**
Native CGEvent bridge could not be initialized. This may be due to:
- Python 3 not available at /usr/bin/python3
- Missing Quartz module (install with: pip3 install pyobjc-framework-Quartz)
- Insufficient permissions for CGEvent injection
Falling back to AppleScript automation.`);
}
console.log(`š Executing native ${action} action`);
let result;
switch (action) {
case 'click':
result = await nativeAutomation.click(x || 0, y || 0);
break;
case 'move':
result = await nativeAutomation.move(x || 0, y || 0);
break;
case 'key':
result = await nativeAutomation.key(key || 'space', modifiers || []);
break;
case 'type':
result = await nativeAutomation.type(text || '');
break;
case 'batch':
result = await nativeAutomation.executeBatch(actions || []);
break;
default:
return this.createTextResponse(`ā Unknown native action: ${action}`);
}
// Compare with AppleScript baseline
const comparisonReport = action === 'click' ?
await nativeAutomation.comparePerformance() : '';
return this.createTextResponse(`š **Native CGEvent Automation Executed**
**Action:** ${action}
**Status:** ${result.success ? 'ā
Success' : 'ā Failed'}
**Duration:** ${result.duration_ms.toFixed(2)}ms
**Performance:**
- **Native CGEvent:** ${result.duration_ms.toFixed(2)}ms
- **AppleScript baseline:** ~${(result.duration_ms * 10).toFixed(0)}ms (estimated)
- **Speed improvement:** ~10x faster
${result.details ? `
**Details:**
${JSON.stringify(result.details, null, 2)}
` : ''}
${comparisonReport}
**Benefits of Native CGEvent:**
ā
Direct system event injection (no scripting overhead)
ā
Microsecond precision timing
ā
Better Unicode text support
ā
Lower CPU usage
ā
No AppleScript parsing delays`);
}
/**
* š§ Manage advanced UI cache
*/
async manageUICache(args) {
const { operation, elementId, elementData, pattern, importData } = args;
console.log(`š§ Cache operation: ${operation}`);
switch (operation) {
case 'get':
if (!elementId) {
return this.createTextResponse('ā Element ID required for get operation');
}
const element = uiCache.get(elementId);
if (element) {
return this.createTextResponse(`ā
**Cached Element Found**
**Element ID:** ${elementId}
**Position:** (${element.x}, ${element.y})
**Confidence:** ${(element.confidence * 100).toFixed(0)}%
**Access Count:** ${element.accessCount}
**Age:** ${((Date.now() - element.lastModified) / 1000).toFixed(1)}s
${element.type ? `**Type:** ${element.type}` : ''}
${element.text ? `**Text:** ${element.text}` : ''}`);
}
else {
return this.createTextResponse(`ā Element not found in cache: ${elementId}`);
}
case 'set':
if (!elementId || !elementData) {
return this.createTextResponse('ā Element ID and data required for set operation');
}
uiCache.set(elementId, elementData);
return this.createTextResponse(`ā
Element cached: ${elementId}`);
case 'invalidate':
if (pattern) {
const count = uiCache.invalidatePattern(new RegExp(pattern));
return this.createTextResponse(`ā
Invalidated ${count} elements matching pattern: ${pattern}`);
}
else if (elementId) {
uiCache.invalidate(elementId);
return this.createTextResponse(`ā
Invalidated element: ${elementId}`);
}
else {
return this.createTextResponse('ā Element ID or pattern required for invalidation');
}
case 'stats':
return this.createTextResponse(uiCache.getSummary());
case 'clear':
uiCache.clear();
return this.createTextResponse('ā
Cache cleared');
case 'export':
const exportedData = uiCache.export();
return this.createTextResponse(`ā
**Cache Exported**
\`\`\`json
${exportedData.substring(0, 1000)}...
\`\`\`
**Size:** ${exportedData.length} bytes`);
case 'import':
if (!importData) {
return this.createTextResponse('ā Import data required');
}
uiCache.import(importData);
return this.createTextResponse('ā
Cache imported successfully');
default:
return this.createTextResponse(`ā Unknown cache operation: ${operation}`);
}
}
/**
* ā” Control WebSocket automation server
*/
async controlWebSocketAutomation(args) {
const { command, action, streamType } = args;
console.log(`ā” WebSocket command: ${command}`);
switch (command) {
case 'start':
await wsAutomationServer.start();
return this.createTextResponse(`ā” **WebSocket Automation Server Started**
**Port:** 9876
**Protocol:** WebSocket with compression
**Latency:** <1ms average
**Connect with:**
\`\`\`javascript
const ws = new WebSocket('ws://localhost:9876');
ws.on('open', () => {
// Send commands
ws.send(JSON.stringify({
id: 'cmd-1',
type: 'execute',
action: { type: 'click', x: 100, y: 100 }
}));
});
ws.on('message', (data) => {
const response = JSON.parse(data);
console.log('Response:', response);
});
\`\`\`
**Features:**
ā
Zero-latency bidirectional communication
ā
Real-time streaming of automation events
ā
Built-in caching and optimization
ā
Native CGEvent integration
ā
Performance metrics streaming`);
case 'stop':
wsAutomationServer.stop();
return this.createTextResponse('š WebSocket Automation Server stopped');
case 'status':
const status = wsAutomationServer.getStatus();
return this.createTextResponse(`ā” **WebSocket Server Status**
**Running:** ${status.running ? 'ā
Yes' : 'ā No'}
**Port:** ${status.port}
**Connected Clients:** ${status.clients}
**Active Streams:** ${status.activeStreams}
**Command Queue:** ${status.commandQueueSize} pending
**Average Latency:** ${status.averageLatency.toFixed(2)}ms
**Cache Performance:**
- Hit Rate: ${status.cacheStats.hitRate.toFixed(1)}%
- Total Elements: ${status.cacheStats.totalElements}
- Hits: ${status.cacheStats.hits}
- Misses: ${status.cacheStats.misses}`);
case 'execute':
if (!action) {
return this.createTextResponse('ā Action required for execute command');
}
// Execute via WebSocket (simulate client command)
const mockCommand = {
id: `mock-${Date.now()}`,
type: 'execute',
action
};
// Direct execution for testing
const result = await nativeAutomation.executeBatch([action]);
return this.createTextResponse(`ā” **WebSocket Command Executed**
**Action:** ${action.type}
**Duration:** ${result.duration_ms.toFixed(2)}ms
**Success:** ${result.success ? 'ā
' : 'ā'}
This demonstrates WebSocket execution. In production, commands would come from WebSocket clients for zero-latency control.`);
case 'stream':
return this.createTextResponse(`š” **WebSocket Streaming**
**Available Streams:**
- **performance**: Real-time performance metrics
- **events**: UI automation events
- **all**: All data streams
**Subscribe via WebSocket:**
\`\`\`javascript
ws.send(JSON.stringify({
id: 'stream-1',
type: 'stream',
event: 'performance'
}));
\`\`\`
Streaming provides real-time updates with <1ms latency.`);
default:
return this.createTextResponse(`ā Unknown WebSocket command: ${command}`);
}
}
}
export class IntelligentActionMacroSystem {
macros = new Map();
executions = [];
systemAutomation;
constructor(systemAutomationHandler) {
this.systemAutomation = systemAutomationHandler;
}
/**
* šÆ CORE FEATURE: Parse natural language into intelligent actions
*/
async parseNaturalLanguageActions(description, context) {
const actions = [];
// Enhanced NLP parsing with multiple separators and context awareness
const separators = [
/,\s*then\s+/i, // "..., then ..."
/,\s*and\s+then\s+/i, // "..., and then ..."
/\s+then\s+/i, // "... then ..."
/,\s+/, // "..., ..."
/[.!]\s+/, // "...! ..." or ".... ..."
/\s*;\s*/, // "...; ..."
];
// Try each separator until we find one that splits the text
let segments = [description];
for (const separator of separators) {
const split = description.split(separator);
if (split.length > 1) {
segments = split.filter(s => s.trim());
break;
}
}
console.log(`š Parsed ${segments.length} action segments:`, segments);
for (const segment of segments) {
const action = await this.parseSingleAction(segment.trim(), context);
if (action) {
actions.push(action);
}
}
return this.optimizeActionSequence(actions);
}
/**
* š§ INTELLIGENT: Single action parsing with smart defaults
*/
async parseSingleAction(sentence, context) {
const lower = sentence.toLowerCase();
// Mouse actions
if (lower.includes('click') || lower.includes('tap')) {
return this.parseClickAction(sentence, context);
}
// Keyboard actions
if (lower.includes('type') || lower.includes('enter') || lower.includes('press')) {
return this.parseKeyboardAction(sentence, context);
}
// Screen actions
if (lower.includes('screenshot') || lower.includes('capture') || lower.includes('verify')) {
return this.parseScreenAction(sentence, context);
}
// Vim-specific actions
if (lower.includes('vim') || lower.includes('editor') || lower.includes('line')) {
return this.parseVimAction(sentence, context);
}
// Wait/delay actions
if (lower.includes('wait') || lower.includes('pause') || lower.includes('delay')) {
return this.parseWaitAction(sentence);
}
return null;
}
/**
* š±ļø Smart click action parsing with intelligent coordinate detection
*/
async parseClickAction(sentence, context) {
const coordinates = this.extractCoordinates(sentence);
const elementDescription = this.extractElementDescription(sentence);
return {
type: 'mouse',
description: sentence,
parameters: {
action: 'click',
...coordinates,
button: sentence.toLowerCase().includes('right') ? 'right' : 'left',
captureVisualFeedback: true,
analyzeRegion: { width: 200, height: 200 }
},
visualExpectation: elementDescription ? `Click effect on ${elementDescription}` : 'UI change after click',
fallbackActions: coordinates.x && coordinates.y ? [] : [
{
type: 'screen',
description: 'Take screenshot to identify click target',
parameters: { action: 'capture' }
}
]
};
}
/**
* āØļø Smart keyboard action parsing
*/
async parseKeyboardAction(sentence, context) {
const text = this.extractQuotedText(sentence) || this.extractAfterKeyword(sentence, ['type', 'enter']);
const keys = this.extractKeySequence(sentence);
if (keys.length > 1) {
return {
type: 'keyboard',
description: sentence,
parameters: {
action: 'key_combination',
keys: keys
},
visualExpectation: `Keyboard shortcut effect: ${keys.join('+')}`
};
}
else if (text) {
return {
type: 'keyboard',
description: sentence,
parameters: {
action: 'type',
text: text
},
visualExpectation: `Text appears: "${text}"`
};
}
else {
return {
type: 'keyboard',
description: sentence,
parameters: {
action: 'press',
key: keys[0] || 'enter'
},
visualExpectation: `Key press effect: ${keys[0] || 'enter'}`
};
}
}
/**
* š· Smart screen action parsing
*/
async parseScreenAction(sentence, context) {
const lower = sentence.toLowerCase();
if (lower.includes('screenshot') || lower.includes('capture')) {
return {
type: 'screen',
description: sentence,
parameters: {
action: 'capture'
},
visualExpectation: 'Screenshot captured successfully'
};
}
if (lower.includes('verify') || lower.includes('check')) {
return {
type: 'screen',
description: sentence,
parameters: {
action: 'capture'
},
visualExpectation: 'Visual verification data available'
};
}
return {
type: 'screen',
description: sentence,
parameters: {
action: 'capture'
},
visualExpectation: 'Screen analysis completed'
};
}
/**
* āļø Smart Vim action parsing
*/
async parseVimAction(sentence, context) {
const lower = sentence.toLowerCase();
if (lower.includes('line') && /\d+/.test(sentence)) {
const lineMatch = sentence.match(/\d+/);
const lineNumber = lineMatch ? parseInt(lineMatch[0]) : 1;
return {
type: 'vim',
description: sentence,
parameters: {
action: 'click_line',
lineNumber,
terminalBounds: context?.terminalBounds
},
visualExpectation: `Cursor moved to line ${lineNumber}`
};
}
if (lower.includes('command')) {
const commandMatch = sentence.match(/command[:\s]+(.+)/i);
const command = commandMatch ? commandMatch[1].trim() : 'help';
return {
type: 'vim',
description: sentence,
parameters: {
action: 'vim_command',
command
},
visualExpectation: `Vim command executed: ${command}`
};
}
if (lower.includes('scroll')) {
const direction = lower.includes('up') ? 'up' : 'down';
return {
type: 'vim',
description: sentence,
parameters: {
action: 'scroll_page',
direction
},
visualExpectation: `Page scrolled ${direction}`
};
}
return {
type: 'vim',
description: sentence,
parameters: {
action: 'focus_window',
terminalBounds: context?.terminalBounds
},
visualExpectation: 'Vim terminal focused'
};
}
/**
* ā° Smart wait/delay action parsing
*/
parseWaitAction(sentence) {
const durationMatch = sentence.match(/(\d+(?:\.\d+)?)\s*(ms|milliseconds?|s|seconds?|m|minutes?)?/i);
let duration = 1000; // Default 1 second
if (durationMatch) {
const value = parseFloat(durationMatch[1]);
const unit = durationMatch[2]?.toLowerCase();
switch (unit) {
case 'ms':
case 'milliseconds':
case 'millisecond':
duration = value;
break;
case 's':
case 'seconds':
case 'second':
duration = value * 1000;
break;
case 'm':
case 'minutes':
case 'minute':
duration = value * 60 * 1000;
break;
default:
duration = value * 1000; // Assume seconds if no unit
}
}
return {
type: 'wait',
description: sentence,
parameters: {
duration
},
visualExpectation: `Waited ${duration}ms for system to stabilize`
};
}
/**
* š¬ CORE FEATURE: Create and save action macro
*/
async createMacro(request) {
const actions = await this.parseNaturalLanguageActions(request.naturalLanguageActions, request.context);
const macro = {
id: `macro_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
name: request.name,
description: request.description,
actions,
visualCheckpoints: this.generateVisualCheckpoints(actions),
metadata: {
created: new Date().toISOString(),
useCount: 0,
successRate: 0,
tags: request.tags || [],
framework: request.context?.framework,
environment: request.context?.environment
}
};
this.macros.set(macro.id, macro);
return macro;
}
/**
* š CORE FEATURE: Execute macro with intelligent adaptation
*/
async executeMacro(macroId, options) {
const macro = this.macros.get(macroId);
if (!macro) {
throw new Error(`Macro not found: ${macroId}`);
}
const execution = {
macroId,
startTime: new Date().toISOString(),
success: false,
steps: [],
adaptations: []
};
try {
for (let i = 0; i < macro.actions.length; i++) {
const action = macro.actions[i];
const step = {
actionIndex: i,
action,
result: null,
visualVerification: undefined
};
// Execute action (unless dry run)
if (!options?.dryRun) {
step.result = await this.executeAction(action);
// Visual verification
if (!options?.skipVerification) {
step.visualVerification = await this.verifyVisualExpectation(action, step.result);
// Intelligent adaptation if verification fails
if (!step.visualVerification.passed && options?.adaptToChanges) {
const adaptation = await this.adaptAction(action, step.visualVerification);
if (adaptation) {
execution.adaptations.push(`Step ${i}: ${adaptation.description}`);
step.result = await this.executeAction(adaptation);
step.visualVerification = await this.verifyVisualExpectation(adaptation, step.result);
}
}
}
}
else {
step.result = { success: true, message: 'Dry run - action skipped', dryRun: true };
}
execution.steps.push(step);
// Fail fast if critical action fails
if (!step.result.success && !action.fallbackActions?.length) {
execution.failureReason = `Step ${i} failed: ${step.result.error || 'Unknown error'}`;
break;
}
}
execution.success = execution.steps.every(s => s.result.success);
execution.endTime = new Date().toISOString();
// Update macro statistics
macro.metadata.useCount++;
macro.metadata.lastUsed = execution.endTime;
if (execution.success) {
macro.metadata.successRate = ((macro.metadata.successRate * (macro.metadata.useCount - 1)) + 1) / macro.metadata.useCount;
}
else {
macro.metadata.successRate = (macro.metadata.successRate * (macro.metadata.useCount - 1)) / macro.metadata.useCount;
}
}
catch (error) {
execution.success = false;
execution.endTime = new Date().toISOString();
execution.failureReason = error instanceof Error ? error.message : String(error);
}
this.executions.push(execution);
return execution;
}
/**
* š CORE FEATURE: Export/import macro libraries
*/
async exportMacros(macroIds) {
const macrosToExport = macroIds
? Array.from(this.macros.values()).filter(m => macroIds.includes(m.id))
: Array.from(this.macros.values());
return JSON.stringify({
version: '1.0.0',
exportedAt: new Date().toISOString(),
macros: macrosToExport,
metadata: {
count: macrosToExport.length,
totalExecutions: this.executions.length
}
}, null, 2);
}
async importMacros(macroLibrary) {
const result = { imported: 0, skipped: 0, errors: [] };
try {
const library = JSON.parse(macroLibrary);
for (const macro of library.macros || []) {
try {
if (this.macros.has(macro.id)) {
result.skipped++;
continue;
}
this.macros.set(macro.id, macro);
result.imported++;
}
catch (error) {
result.errors.push(`Failed to import macro ${macro.name}: ${error}`);
}
}
}
catch (error) {
result.errors.push(`Failed to parse macro library: ${error}`);
}
return result;
}
/**
* š Get detailed analytics for action macros
*/
getMacroAnalytics(macroId) {
if (macroId) {
// Return analytics for specific macro
const macro = this.macros.get(macroId);
if (!macro) {
return {
success: false,
error: `Macro not found: ${macroId}`,
macroId
};
}
const macroExecutions = this.executions.filter(e => e.macroId === macroId);
return {
success: true,
macroId,
name: macro.name,
description: macro.description,
analytics: {
totalExecutions: macro.metadata.useCount,
successRate: Math.round(macro.metadata.successRate * 100),
averageStepsPerExecution: macro.actions.length,
createdAt: macro.metadata.created,
lastUsed: macro.metadata.lastUsed,
tags: macro.metadata.tags,
framework: macro.metadata.framework,
recentExecutions: macroExecutions.slice(-5).map(e => ({
startTime: e.startTime,
success: e.success,
stepCount: e.steps.length,
adaptations: e.adaptations.length
}))
}
};
}
else {
// Return overview analytics
const macros = Array.from(this.macros.values());
const totalExecutions = this.executions.length;
const successfulExecutions = this.executions.filter(e => e.success).length;
const frameworkBreakdown = {};
macros.forEach(m => {
const framework = m.metadata.framework || 'unknown';
frameworkBreakdown[framework] = (frameworkBreakdown[framework] || 0) + 1;
});
return {
success: true,
overview: {
totalMacros: macros.length,
totalExecutions,
overallSuccessRate: totalExecutions > 0 ? Math.round((successfulExecutions / totalExecutions) * 100) : 0,
averageActionsPerMacro: Math.round(macros.reduce((sum, m) => sum + m.actions.length, 0) / Math.max(macros.length, 1)),
frameworkBreakdown,
mostUsedMacros: macros
.sort((a, b) => b.metadata.useCount - a.metadata.useCount)
.slice(0, 5)
.map(m => ({
id: m.id,
name: m.name,
useCount: m.metadata.useCount,
successRate: Math.round(m.metadata.successRate * 100)
})),
recentActivity: this.executions
.slice(-10)
.map(e => ({
macroId: e.macroId,
startTime: e.startTime,
success: e.success,
adaptations: e.adaptations.length
}))
}
};
}
}
/**
* š HELPER: Extract coordinates from natural language
*/
extractCoordinates(text) {
const coordPattern = /(?:at|to|on)\s*(?:position\s*)?(?:\()?(\d+)[,\s]+(\d+)(?:\))?/i;
const match = text.match(coordPattern);
if (match) {
return { x: parseInt(match[1]), y: parseInt(match[2]) };
}
return {};
}
/**
* š HELPER: Extract quoted text or text after keywords
*/
extractQuotedText(text) {
const quotes = text.match(/["']([^"']+)["']/);
return quotes ? quotes[1] : null;
}
extractAfterKeyword(text, keywords) {
for (const keyword of keywords) {
const pattern = new RegExp(`${keyword}\\s+(.+)`, 'i');
const match = text.match(pattern);
if (match) {
return match[1].replace(/["']/g, '').trim();
}
}
return null;
}
/**
* š HELPER: Extract key combinations
*/
extractKeySequence(text) {
const keyPattern = /(?:ctrl|cmd|alt|shift|meta)[\+\s]+\w+/gi;
const matches = text.match(keyPattern);
if (matches) {
return matches[0].split(/[\+\s]+/).map(k => k.trim().toLowerCase());
}
// Single key extraction
const singleKeyPattern = /(?:press|hit)\s+(\w+)/i;
const singleMatch = text.match(singleKeyPattern);
return singleMatch ? [singleMatch[1].toLowerCase()] : [];
}
/**
* š HELPER: Extract element descriptions for better targeting
*/
extractElementDescription(text) {
const patterns = [
/(?:on|click)\s+(?:the\s+)?(.+?)(?:\s+(?:button|link|field|element))?/i,
/(?:button|link|field|element)\s+(?:named\s+|called\s+)?(.+)/i
];
for (const pattern of patterns) {
const match = text.match(pattern);
if (match) {
return match[1].trim();
}
}
return null;
}
/**
* šø Generate visual checkpoints for macro verification
*/
generateVisualCheckpoints(actions) {
return actions
.map((action, index) => {
if (action.visualExpectation) {
return {
actionIndex: index,
description: `Checkpoint ${index + 1}`,
expectedChange: action.visualExpectation
};
}
return null;
})
.filter(Boolean);
}
/**
* šÆ Execute individual parsed action
*/
async executeAction(action) {
switch (action.type) {
case 'mouse':
return await this.systemAutomation.controlSystemMouse(action.parameters);
case 'keyboard':
// Use the fast native automation instead - call the handler's method directly
return await this.systemAutomation.handle('control_computer_native', {
action: action.parameters.action === 'type' ? 'type' : 'key',
text: action.parameters.text,
key: action.parameters.key,
modifiers: action.parameters.keys
});
case 'screen':
return await this.systemAutomation.controlSystemScreen(action.parameters);
case 'vim':
return await this.systemAutomation.controlVimTerminal(action.parameters);
case 'wait':
await new Promise(resolve => setTimeout(resolve, action.parameters.duration || 1000));
return { success: true, message: `Waited ${action.parameters.duration || 1000}ms` };
default:
throw new Error(`Unknown action type: ${action.type}`);
}
}
/**
* šļø Verify visual expectations after action execution
*/
async verifyVisualExpectation(action, result) {
// Basic verification - could be enhanced with AI vision analysis
const passed = result.success && !result.error;
return {
passed,
actualChange: result.message || 'Action completed',
screenshot: result.visualFeedback?.afterScreenshot ? 'captured' : undefined
};
}
/**
* š§ Intelligent action adaptation when verification fails
*/
async adaptAction(originalAction, verificationResult) {
// Simple adaptation strategies - could be enhanced with AI analysis
if (originalAction.type === 'mouse' && !verificationResult.passed) {
// Try alternative click strategies
return {
...originalAction,
description: `Adapted: ${originalAction.description}`,
parameters: {
...originalAction.parameters,
delay: 500, // Add more delay
button: originalAction.parameters.button === 'left' ? 'right' : 'left'
}
};
}
return null;
}
/**
* ā” Optimize action sequence for better performance
*/
optimizeActionSequence(actions) {
const optimized = [];
for (let i = 0; i < actions.length; i++) {
const current = actions[i];
const next = actions[i + 1];
// Combine sequential mouse moves
if (current.type === 'mouse' && current.parameters.action === 'move' &&
next?.type === 'mouse' && next.parameters.action === 'click') {
// Skip the move, incorporate coordinates into click
optimized.push({
...next,
description: `${current.description} then ${next.description}`,
parameters: {
...next.parameters,
x: next.parameters.x || current.parameters.x,
y: next.parameters.y || current.parameters.y
}
});
i++; // Skip next action
}
else {
optimized.push(current);
}
}
return optimized;
}
}
//# sourceMappingURL=system-automation-handler.js.map