UNPKG

cui-server

Version:

Web UI Agent Platform based on Claude Code

161 lines 5.59 kB
import { EventEmitter } from 'events'; import { v4 as uuidv4 } from 'uuid'; import { logger } from '../services/logger.js'; /** * Service to track permission requests from Claude CLI via MCP */ export class PermissionTracker extends EventEmitter { permissionRequests = new Map(); notificationService; conversationStatusManager; historyReader; constructor() { super(); } /** * Set the notification service */ setNotificationService(service) { this.notificationService = service; } /** * Set the conversation status manager */ setConversationStatusManager(manager) { this.conversationStatusManager = manager; } /** * Set the history reader */ setHistoryReader(reader) { this.historyReader = reader; } /** * Add a new permission request */ addPermissionRequest(toolName, toolInput, streamingId) { const id = uuidv4(); const request = { id, streamingId: streamingId || 'unknown', toolName, toolInput, timestamp: new Date().toISOString(), status: 'pending', }; this.permissionRequests.set(id, request); logger.info('Permission request added', { id, toolName, streamingId }); // Emit event for new permission request this.emit('permission_request', request); // Send notification if services are available if (this.notificationService && this.conversationStatusManager && this.historyReader) { // Get session ID from streaming ID const sessionId = this.conversationStatusManager.getSessionId(streamingId || ''); if (sessionId) { // Try to get conversation summary this.historyReader.getConversationMetadata(sessionId) .then(metadata => { if (this.notificationService) { return this.notificationService.sendPermissionNotification(request, sessionId, metadata?.summary); } }) .catch(error => { logger.error('Failed to fetch conversation metadata for notification', error); // Fall back to sending without summary if (this.notificationService) { this.notificationService.sendPermissionNotification(request, sessionId) .catch(err => logger.error('Failed to send permission notification', err)); } }); } else { // No session ID available, send without session info this.notificationService.sendPermissionNotification(request) .catch(error => { logger.error('Failed to send permission notification', error); }); } } return request; } /** * Get all permission requests */ getAllPermissionRequests() { return Array.from(this.permissionRequests.values()); } /** * Get permission requests filtered by criteria */ getPermissionRequests(filter) { let requests = Array.from(this.permissionRequests.values()); if (filter?.streamingId) { requests = requests.filter(req => req.streamingId === filter.streamingId); } if (filter?.status) { requests = requests.filter(req => req.status === filter.status); } return requests; } /** * Get a specific permission request by ID */ getPermissionRequest(id) { return this.permissionRequests.get(id); } /** * Update permission request status (for future use when we implement approval/denial) */ updatePermissionStatus(id, status, options) { const request = this.permissionRequests.get(id); if (!request) { logger.warn('Permission request not found', { id }); return false; } request.status = status; if (status === 'approved' && options?.modifiedInput) { request.modifiedInput = options.modifiedInput; } if (status === 'denied' && options?.denyReason) { request.denyReason = options.denyReason; } logger.info('Permission request updated', { id, status }); this.emit('permission_updated', request); return true; } /** * Clear all permission requests (for testing) */ clear() { this.permissionRequests.clear(); } /** * Get the number of permission requests */ size() { return this.permissionRequests.size; } /** * Remove all permissions for a specific streaming ID * Used for cleanup when a conversation ends */ removePermissionsByStreamingId(streamingId) { const toRemove = []; // Find all permissions with this streamingId for (const [id, request] of this.permissionRequests.entries()) { if (request.streamingId === streamingId) { toRemove.push(id); } } // Remove them toRemove.forEach(id => this.permissionRequests.delete(id)); if (toRemove.length > 0) { logger.info('Removed permissions for streaming session', { streamingId, removedCount: toRemove.length }); } return toRemove.length; } } //# sourceMappingURL=permission-tracker.js.map