diff --git a/src/main/index.ts b/src/main/index.ts index 6bd43bfd..2539c961 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -154,7 +154,7 @@ function initializeServices(): void { updaterService = new UpdaterService(); // Initialize IPC handlers with registry - initializeIpcHandlers(contextRegistry, updaterService, sshConnectionManager); + initializeIpcHandlers(contextRegistry, updaterService, sshConnectionManager, onContextSwitched); // Forward SSH state changes to renderer sshConnectionManager.on('state-change', (status: unknown) => { diff --git a/src/main/ipc/context.ts b/src/main/ipc/context.ts new file mode 100644 index 00000000..c2894432 --- /dev/null +++ b/src/main/ipc/context.ts @@ -0,0 +1,97 @@ +/** + * Context IPC Handlers - Manages context switching and listing. + * + * Channels: + * - context:list - List all available contexts (local + SSH) + * - context:getActive - Get current active context ID + * - context:switch - Switch to a different context + */ + +import { + CONTEXT_CHANGED, + CONTEXT_GET_ACTIVE, + CONTEXT_LIST, + CONTEXT_SWITCH, +} from '@preload/constants/ipcChannels'; +import { createLogger } from '@shared/utils/logger'; + +import type { ServiceContext, ServiceContextRegistry } from '../services'; +import type { IpcMain } from 'electron'; + +const logger = createLogger('IPC:context'); + +// ============================================================================= +// Module State +// ============================================================================= + +let registry: ServiceContextRegistry; +let onContextSwitched: (context: ServiceContext) => void; + +// ============================================================================= +// Initialization +// ============================================================================= + +/** + * Initialize context handlers with required services. + * @param contextRegistry - The service context registry + * @param onSwitched - Callback to invoke after successful context switch + */ +export function initializeContextHandlers( + contextRegistry: ServiceContextRegistry, + onSwitched: (context: ServiceContext) => void +): void { + registry = contextRegistry; + onContextSwitched = onSwitched; +} + +// ============================================================================= +// Handler Registration +// ============================================================================= + +export function registerContextHandlers(ipcMain: IpcMain): void { + ipcMain.handle(CONTEXT_LIST, async () => { + try { + const contexts = registry.list(); + return { success: true, data: contexts }; + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + logger.error('Failed to list contexts:', message); + return { success: false, error: message }; + } + }); + + ipcMain.handle(CONTEXT_GET_ACTIVE, async () => { + try { + const activeContextId = registry.getActiveContextId(); + return { success: true, data: activeContextId }; + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + logger.error('Failed to get active context:', message); + return { success: false, error: message }; + } + }); + + ipcMain.handle(CONTEXT_SWITCH, async (_event, contextId: string) => { + try { + // Switch to the new context + const { current } = registry.switch(contextId); + + // Invoke the context switched callback (re-wires file watcher events) + onContextSwitched(current); + + return { success: true, data: { contextId } }; + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + logger.error(`Context switch to "${contextId}" failed:`, message); + return { success: false, error: message }; + } + }); + + logger.info('Context handlers registered'); +} + +export function removeContextHandlers(ipcMain: IpcMain): void { + ipcMain.removeHandler(CONTEXT_LIST); + ipcMain.removeHandler(CONTEXT_GET_ACTIVE); + ipcMain.removeHandler(CONTEXT_SWITCH); +} diff --git a/src/main/ipc/handlers.ts b/src/main/ipc/handlers.ts index 169917b7..1ba70ad9 100644 --- a/src/main/ipc/handlers.ts +++ b/src/main/ipc/handlers.ts @@ -17,6 +17,11 @@ import { createLogger } from '@shared/utils/logger'; import { ipcMain } from 'electron'; import { registerConfigHandlers, removeConfigHandlers } from './config'; +import { + initializeContextHandlers, + registerContextHandlers, + removeContextHandlers, +} from './context'; const logger = createLogger('IPC:handlers'); import { registerNotificationHandlers, removeNotificationHandlers } from './notifications'; @@ -45,7 +50,12 @@ import { import { registerUtilityHandlers, removeUtilityHandlers } from './utility'; import { registerValidationHandlers, removeValidationHandlers } from './validation'; -import type { ServiceContextRegistry, SshConnectionManager, UpdaterService } from '../services'; +import type { + ServiceContext, + ServiceContextRegistry, + SshConnectionManager, + UpdaterService, +} from '../services'; /** * Initializes IPC handlers with service registry. @@ -53,7 +63,8 @@ import type { ServiceContextRegistry, SshConnectionManager, UpdaterService } fro export function initializeIpcHandlers( registry: ServiceContextRegistry, updater: UpdaterService, - sshManager: SshConnectionManager + sshManager: SshConnectionManager, + onContextSwitched: (context: ServiceContext) => void ): void { // Initialize domain handlers with registry initializeProjectHandlers(registry); @@ -62,6 +73,7 @@ export function initializeIpcHandlers( initializeSubagentHandlers(registry); initializeUpdaterHandlers(updater); initializeSshHandlers(sshManager, registry); + initializeContextHandlers(registry, onContextSwitched); // Register all handlers registerProjectHandlers(ipcMain); @@ -74,6 +86,7 @@ export function initializeIpcHandlers( registerConfigHandlers(ipcMain); registerUpdaterHandlers(ipcMain); registerSshHandlers(ipcMain); + registerContextHandlers(ipcMain); logger.info('All handlers registered'); } @@ -93,6 +106,7 @@ export function removeIpcHandlers(): void { removeConfigHandlers(ipcMain); removeUpdaterHandlers(ipcMain); removeSshHandlers(ipcMain); + removeContextHandlers(ipcMain); logger.info('All handlers removed'); } diff --git a/src/preload/constants/ipcChannels.ts b/src/preload/constants/ipcChannels.ts index a3983326..9b190f31 100644 --- a/src/preload/constants/ipcChannels.ts +++ b/src/preload/constants/ipcChannels.ts @@ -105,3 +105,19 @@ export const UPDATER_INSTALL = 'updater:install'; /** Status event channel (main -> renderer) */ export const UPDATER_STATUS = 'updater:status'; + +// ============================================================================= +// Context API Channels +// ============================================================================= + +/** List all available contexts (local + SSH) */ +export const CONTEXT_LIST = 'context:list'; + +/** Get active context ID */ +export const CONTEXT_GET_ACTIVE = 'context:getActive'; + +/** Switch to a different context */ +export const CONTEXT_SWITCH = 'context:switch'; + +/** Context changed event channel (main -> renderer) */ +export const CONTEXT_CHANGED = 'context:changed';