From bd198a78ad74ea46defc8da84e21a0b5147c2c3c Mon Sep 17 00:00:00 2001 From: Marc Mintel Date: Sat, 22 Nov 2025 01:50:37 +0100 Subject: [PATCH] fix(automation): add stopAutomation to IAutomationEngine interface and fix interval cleanup --- apps/companion/main/ipc-handlers.ts | 54 +++++++++++++++---- .../application/ports/IAutomationEngine.ts | 1 + 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/apps/companion/main/ipc-handlers.ts b/apps/companion/main/ipc-handlers.ts index 33b09a12c..f53d685d5 100644 --- a/apps/companion/main/ipc-handlers.ts +++ b/apps/companion/main/ipc-handlers.ts @@ -3,35 +3,53 @@ 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'; -import { MockAutomationEngineAdapter } from '@/packages/infrastructure/adapters/automation/MockAutomationEngineAdapter'; + +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 logger = container.getLogger(); 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; + } + // 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 - const checkInterval = setInterval(async () => { + progressMonitorInterval = setInterval(async () => { const updatedSession = await sessionRepository.findById(result.sessionId); if (!updatedSession) { - clearInterval(checkInterval); + if (progressMonitorInterval) { + clearInterval(progressMonitorInterval); + progressMonitorInterval = null; + } return; } @@ -44,10 +62,14 @@ export function setupIpcHandlers(mainWindow: BrowserWindow): void { errorMessage: updatedSession.errorMessage || null }); - if (updatedSession.state.value === 'COMPLETED' || + if (updatedSession.state.value === 'COMPLETED' || updatedSession.state.value === 'FAILED' || updatedSession.state.value === 'STOPPED_AT_STEP_18') { - clearInterval(checkInterval); + logger.info('Automation ended', { state: updatedSession.state.value }); + if (progressMonitorInterval) { + clearInterval(progressMonitorInterval); + progressMonitorInterval = null; + } } }, 100); @@ -56,9 +78,11 @@ export function setupIpcHandlers(mainWindow: BrowserWindow): void { sessionId: result.sessionId }; } catch (error) { + const err = error instanceof Error ? error : new Error('Unknown error'); + logger.error('Automation failed', err); return { success: false, - error: error instanceof Error ? error.message : 'Unknown error' + error: err.message }; } }); @@ -89,22 +113,34 @@ export function setupIpcHandlers(mainWindow: BrowserWindow): void { 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 - const engine = automationEngine as MockAutomationEngineAdapter; - engine.stopAutomation(); + 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: error instanceof Error ? error.message : 'Unknown error' + error: err.message }; } }); diff --git a/packages/application/ports/IAutomationEngine.ts b/packages/application/ports/IAutomationEngine.ts index 5d54f59af..553824efb 100644 --- a/packages/application/ports/IAutomationEngine.ts +++ b/packages/application/ports/IAutomationEngine.ts @@ -9,4 +9,5 @@ export interface ValidationResult { export interface IAutomationEngine { validateConfiguration(config: HostedSessionConfig): Promise; executeStep(stepId: StepId, config: HostedSessionConfig): Promise; + stopAutomation(): void; } \ No newline at end of file