import { ipcMain } from 'electron'; import type { BrowserWindow, IpcMainInvokeEvent } from 'electron'; import { DIContainer } from './di-container'; import type { HostedSessionConfig } from '@/packages/domain/entities/HostedSessionConfig'; import { StepId } from '@/packages/domain/value-objects/StepId'; let progressMonitorInterval: NodeJS.Timeout | null = null; export function setupIpcHandlers(mainWindow: BrowserWindow): void { const container = DIContainer.getInstance(); const startAutomationUseCase = container.getStartAutomationUseCase(); const sessionRepository = container.getSessionRepository(); const automationEngine = container.getAutomationEngine(); const permissionService = container.getPermissionService(); const logger = container.getLogger(); // Permission handlers ipcMain.handle('automation:checkPermissions', async () => { try { const result = await permissionService.checkPermissions(); return { success: true, granted: result.granted, status: result.status, missingPermissions: result.missingPermissions, }; } catch (error) { const err = error instanceof Error ? error : new Error('Unknown error'); logger.error('Permission check failed', err); return { success: false, error: err.message, granted: false, status: { accessibility: false, screenRecording: false, platform: process.platform, }, missingPermissions: ['Accessibility', 'Screen Recording'], }; } }); ipcMain.handle('automation:requestAccessibility', async () => { try { const granted = permissionService.requestAccessibilityPermission(); return { success: true, granted }; } catch (error) { const err = error instanceof Error ? error : new Error('Unknown error'); logger.error('Accessibility permission request failed', err); return { success: false, granted: false, error: err.message }; } }); ipcMain.handle('automation:openPermissionSettings', async (_event: IpcMainInvokeEvent, pane?: 'accessibility' | 'screenRecording') => { try { if (pane) { await permissionService.openSystemPreferences(pane); } else { await permissionService.openPermissionsSettings(); } return { success: true }; } catch (error) { const err = error instanceof Error ? error : new Error('Unknown error'); logger.error('Failed to open permission settings', err); return { success: false, error: err.message }; } }); ipcMain.handle('start-automation', async (_event: IpcMainInvokeEvent, config: HostedSessionConfig) => { try { logger.info('Starting automation', { sessionName: config.sessionName }); // Clear any existing progress interval if (progressMonitorInterval) { clearInterval(progressMonitorInterval); progressMonitorInterval = null; } // Check permissions before starting automation (macOS only) const permissionResult = await permissionService.checkPermissions(); if (!permissionResult.granted) { logger.warn('Automation blocked due to missing permissions', { missingPermissions: permissionResult.missingPermissions, }); return { success: false, error: `Missing required permissions: ${permissionResult.missingPermissions.join(', ')}. Please grant permissions in System Preferences and try again.`, permissionError: true, missingPermissions: permissionResult.missingPermissions, }; } // Connect to browser first (required for dev mode) const connectionResult = await container.initializeBrowserConnection(); if (!connectionResult.success) { logger.error('Browser connection failed', undefined, { errorMessage: connectionResult.error }); return { success: false, error: connectionResult.error }; } logger.info('Browser connection established'); const result = await startAutomationUseCase.execute(config); logger.info('Automation session created', { sessionId: result.sessionId }); const session = await sessionRepository.findById(result.sessionId); if (session) { // Start the automation by executing step 1 logger.info('Executing step 1'); await automationEngine.executeStep(StepId.create(1), config); } // Set up progress monitoring progressMonitorInterval = setInterval(async () => { const updatedSession = await sessionRepository.findById(result.sessionId); if (!updatedSession) { if (progressMonitorInterval) { clearInterval(progressMonitorInterval); progressMonitorInterval = null; } return; } mainWindow.webContents.send('session-progress', { sessionId: result.sessionId, currentStep: updatedSession.currentStep.value, state: updatedSession.state.value, completedSteps: Array.from({ length: updatedSession.currentStep.value - 1 }, (_, i) => i + 1), hasError: updatedSession.errorMessage !== undefined, errorMessage: updatedSession.errorMessage || null }); if (updatedSession.state.value === 'COMPLETED' || updatedSession.state.value === 'FAILED' || updatedSession.state.value === 'STOPPED_AT_STEP_18') { logger.info('Automation ended', { state: updatedSession.state.value }); if (progressMonitorInterval) { clearInterval(progressMonitorInterval); progressMonitorInterval = null; } } }, 100); return { success: true, sessionId: result.sessionId }; } catch (error) { const err = error instanceof Error ? error : new Error('Unknown error'); logger.error('Automation failed', err); return { success: false, error: err.message }; } }); ipcMain.handle('get-session-status', async (_event: IpcMainInvokeEvent, sessionId: string) => { const session = await sessionRepository.findById(sessionId); if (!session) { return { found: false }; } return { found: true, currentStep: session.currentStep.value, state: session.state.value, completedSteps: Array.from({ length: session.currentStep.value - 1 }, (_, i) => i + 1), hasError: session.errorMessage !== undefined, errorMessage: session.errorMessage || null }; }); ipcMain.handle('pause-automation', async (_event: IpcMainInvokeEvent, _sessionId: string) => { return { success: false, error: 'Pause not implemented in POC' }; }); ipcMain.handle('resume-automation', async (_event: IpcMainInvokeEvent, _sessionId: string) => { return { success: false, error: 'Resume not implemented in POC' }; }); ipcMain.handle('stop-automation', async (_event: IpcMainInvokeEvent, sessionId: string) => { try { logger.info('Stopping automation', { sessionId }); // Clear progress monitoring interval if (progressMonitorInterval) { clearInterval(progressMonitorInterval); progressMonitorInterval = null; logger.info('Progress monitor cleared'); } // Stop the automation engine interval automationEngine.stopAutomation(); logger.info('Automation engine stopped'); // Update session state to failed with user stop reason const session = await sessionRepository.findById(sessionId); if (session) { session.fail('User stopped automation'); await sessionRepository.update(session); logger.info('Session marked as failed', { sessionId }); } return { success: true }; } catch (error) { const err = error instanceof Error ? error : new Error('Unknown error'); logger.error('Stop automation failed', err); return { success: false, error: err.message }; } }); }