UNPKG

anon-identity

Version:

Decentralized identity framework with DIDs, Verifiable Credentials, and privacy-preserving selective disclosure

294 lines 11.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CommunicationManager = void 0; const types_1 = require("./types"); const message_protocol_1 = require("./message-protocol"); const message_handler_1 = require("./message-handler"); class CommunicationManager { constructor(agentIdentity, agentManager, delegationManager, policyEngine, activityLogger, options = {}) { this.agentIdentity = agentIdentity; this.channels = new Map(); this.stats = new Map(); this.pendingMessages = new Map(); this.options = { enableEncryption: options.enableEncryption || false, defaultMessageTTL: options.defaultMessageTTL || 300000, // 5 minutes maxRetries: options.maxRetries || 3, retryDelay: options.retryDelay || 5000, enableStats: options.enableStats || true }; this.messageHandlers = new message_handler_1.MessageHandlerRegistry(agentManager, delegationManager, policyEngine, activityLogger); this.initializeStats(); } /** * Adds a communication channel */ addChannel(channel) { this.channels.set(channel.id, channel); // Set up message handling for this channel channel.onMessage(async (envelope) => { await this.handleIncomingMessage(envelope, channel.id); }); // Initialize stats for this channel if (this.options.enableStats) { this.stats.set(channel.id, { messagesSent: 0, messagesReceived: 0, messagesDropped: 0, averageResponseTime: 0, errorRate: 0 }); } } /** * Removes a communication channel */ async removeChannel(channelId) { const channel = this.channels.get(channelId); if (channel) { await channel.disconnect(); this.channels.delete(channelId); this.stats.delete(channelId); } } /** * Connects all channels */ async connectAll() { const connections = Array.from(this.channels.values()).map(channel => channel.connect().catch(error => { console.error(`Failed to connect channel ${channel.id}:`, error); return error; })); await Promise.allSettled(connections); } /** * Disconnects all channels */ async disconnectAll() { const disconnections = Array.from(this.channels.values()).map(channel => channel.disconnect().catch(error => { console.error(`Failed to disconnect channel ${channel.id}:`, error); return error; })); await Promise.allSettled(disconnections); } /** * Sends a message through the best available channel */ async sendMessage(message, preferredChannelId) { // Sign the message const signedMessage = await message_protocol_1.MessageProtocol.signMessage(message, this.agentIdentity.keyPair); // Create envelope const envelope = message_protocol_1.MessageProtocol.createEnvelope(signedMessage, { ttl: this.options.defaultMessageTTL }); // Choose channel const channel = this.selectChannel(message.to, preferredChannelId); if (!channel) { throw new Error(`No available channel for recipient: ${message.to}`); } try { await this.sendThroughChannel(envelope, channel); this.updateStats(channel.id, 'sent'); } catch (error) { this.updateStats(channel.id, 'error'); // Add to retry queue if retries are enabled if (this.options.maxRetries > 0) { this.pendingMessages.set(message.id, { envelope, retries: 0, lastAttempt: new Date() }); } throw error; } } /** * Sends a delegation request to another agent */ async requestDelegation(targetAgentDID, requestedScopes, options = {}) { const message = message_protocol_1.MessageProtocol.createMessage(types_1.AgentMessageType.DELEGATION_REQUEST, this.agentIdentity.did, targetAgentDID, { requestedScopes, serviceDID: options.serviceDID, duration: options.duration, purpose: options.purpose }); await this.sendMessage(message, options.channelId); } /** * Queries another agent's status */ async queryAgentStatus(targetAgentDID, options = {}) { const message = message_protocol_1.MessageProtocol.createMessage(types_1.AgentMessageType.QUERY_STATUS, this.agentIdentity.did, targetAgentDID, { includeChain: options.includeChain, includeScopes: options.includeScopes, includeMetrics: options.includeMetrics }); await this.sendMessage(message, options.channelId); } /** * Pings another agent */ async pingAgent(targetAgentDID, channelId) { const message = message_protocol_1.MessageProtocol.createMessage(types_1.AgentMessageType.PING, this.agentIdentity.did, targetAgentDID, {}); await this.sendMessage(message, channelId); } /** * Registers a custom message handler */ registerMessageHandler(type, handler) { this.messageHandlers.registerHandler(type, handler); } /** * Gets communication statistics */ getStats(channelId) { if (channelId) { return this.stats.get(channelId) || { messagesSent: 0, messagesReceived: 0, messagesDropped: 0, averageResponseTime: 0, errorRate: 0 }; } return this.stats; } /** * Gets list of connected channels */ getConnectedChannels() { return Array.from(this.channels.values()) .filter(channel => channel.isConnected) .map(channel => channel.id); } /** * Retries failed messages */ async retryFailedMessages() { let retriedCount = 0; const now = new Date(); for (const [messageId, pending] of this.pendingMessages.entries()) { // Check if enough time has passed for retry if (now.getTime() - pending.lastAttempt.getTime() >= this.options.retryDelay) { if (pending.retries < this.options.maxRetries) { try { const channel = this.selectChannel(pending.envelope.message.to); if (channel) { await this.sendThroughChannel(pending.envelope, channel); this.pendingMessages.delete(messageId); retriedCount++; } else { pending.retries++; pending.lastAttempt = now; } } catch (error) { pending.retries++; pending.lastAttempt = now; if (pending.retries >= this.options.maxRetries) { this.pendingMessages.delete(messageId); } } } else { // Max retries reached, remove from queue this.pendingMessages.delete(messageId); } } } return retriedCount; } // Private methods async handleIncomingMessage(envelope, channelId) { try { // Validate and process envelope const { message, errors } = await message_protocol_1.MessageProtocol.processEnvelope(envelope, this.agentIdentity.keyPair); if (errors && errors.length > 0) { console.error('Message processing errors:', errors); this.updateStats(channelId, 'dropped'); return; } // Update stats this.updateStats(channelId, 'received'); // Process message through handlers const response = await this.messageHandlers.processMessage(message, this.agentIdentity, (responseMessage) => this.sendMessage(responseMessage, channelId)); // Send response if one was generated if (response) { await this.sendMessage(response, channelId); } } catch (error) { console.error('Error handling incoming message:', error); this.updateStats(channelId, 'error'); } } selectChannel(recipientDID, preferredChannelId) { // Try preferred channel first if (preferredChannelId) { const preferred = this.channels.get(preferredChannelId); if (preferred && preferred.isConnected) { return preferred; } } // Find best available channel const connectedChannels = Array.from(this.channels.values()) .filter(channel => channel.isConnected); if (connectedChannels.length === 0) { return null; } // Simple selection: prefer direct channels, then websockets const direct = connectedChannels.find(ch => ch.type === 'direct'); if (direct) return direct; const websocket = connectedChannels.find(ch => ch.type === 'websocket'); if (websocket) return websocket; // Return first available return connectedChannels[0]; } async sendThroughChannel(envelope, channel) { if (!channel.isConnected) { throw new Error(`Channel ${channel.id} is not connected`); } await channel.send(envelope); } updateStats(channelId, type) { if (!this.options.enableStats) return; const stats = this.stats.get(channelId); if (!stats) return; switch (type) { case 'sent': stats.messagesSent++; break; case 'received': stats.messagesReceived++; stats.lastMessageTime = new Date(); break; case 'dropped': stats.messagesDropped++; break; case 'error': const totalMessages = stats.messagesSent + stats.messagesReceived; if (totalMessages > 0) { stats.errorRate = (stats.messagesDropped + 1) / totalMessages; } break; } } initializeStats() { if (!this.options.enableStats) return; // Set up periodic retry of failed messages setInterval(() => { this.retryFailedMessages().catch(error => { console.error('Error retrying failed messages:', error); }); }, this.options.retryDelay); } } exports.CommunicationManager = CommunicationManager; //# sourceMappingURL=communication-manager.js.map