UNPKG

anon-identity

Version:

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

440 lines 14.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ActivityLogger = void 0; exports.getActivityLogger = getActivityLogger; exports.createActivity = createActivity; const uuid_1 = require("uuid"); const ipfs_activity_storage_1 = require("./ipfs-activity-storage"); const ipfs_redundancy_manager_1 = require("./ipfs-redundancy-manager"); const activity_index_1 = require("./activity-index"); const activity_stream_manager_1 = require("./activity-stream-manager"); /** * ActivityLogger - Core service for logging agent activities * * Provides activity tracking, batching, and real-time streaming */ class ActivityLogger { constructor(config = {}) { this.activityBuffer = []; this.subscriptions = new Map(); this.hooks = new Map(); this.processingBatch = false; this.config = { batchSize: config.batchSize || 100, batchInterval: config.batchInterval || 5000, enableRealtime: config.enableRealtime ?? true, enableBatching: config.enableBatching ?? true, retentionDays: config.retentionDays || 90, encryptionKey: config.encryptionKey, enableIPFS: config.enableIPFS ?? false, ipfsUrl: config.ipfsUrl, enableRedundancy: config.enableRedundancy ?? false, ipfsNodes: config.ipfsNodes, minReplicas: config.minReplicas || 2, enableIndexing: config.enableIndexing ?? true, enableStreaming: config.enableStreaming ?? false }; if (this.config.enableBatching) { this.startBatchTimer(); } // Initialize IPFS storage if (this.config.enableIPFS) { if (this.config.enableRedundancy && this.config.ipfsNodes) { // Use redundancy manager for multiple nodes this.redundancyManager = new ipfs_redundancy_manager_1.IPFSRedundancyManager({ nodes: this.config.ipfsNodes, minReplicas: this.config.minReplicas || 2, encryptionKey: this.config.encryptionKey }); } else { // Use single node storage this.ipfsStorage = new ipfs_activity_storage_1.IPFSActivityStorage({ url: this.config.ipfsUrl, encryptionKey: this.config.encryptionKey }); } } // Initialize activity index if (this.config.enableIndexing) { this.activityIndex = new activity_index_1.ActivityIndex(); } // Initialize stream manager if (this.config.enableStreaming) { this.streamManager = new activity_stream_manager_1.ActivityStreamManager({ enableAlerts: true, enableMetrics: true }); } } /** * Log a single activity */ async logActivity(activity) { // Generate ID and timestamp if not provided const fullActivity = { id: activity.id || (0, uuid_1.v4)(), timestamp: activity.timestamp || new Date(), scopes: activity.scopes || [], details: activity.details || {}, ...activity }; // Run before hooks const shouldContinue = await this.runBeforeHooks(fullActivity); if (!shouldContinue) { throw new Error('Activity rejected by hook'); } // Add to buffer if batching is enabled if (this.config.enableBatching) { this.activityBuffer.push(fullActivity); // Check if we should process the batch immediately if (this.activityBuffer.length >= this.config.batchSize) { await this.processBatch(); } } // Notify real-time subscribers if (this.config.enableRealtime) { await this.notifySubscribers(fullActivity); } // Index the activity for searching if (this.activityIndex) { try { await this.activityIndex.indexActivity(fullActivity); } catch (error) { console.error('Failed to index activity:', error); } } // Stream the activity for real-time updates if (this.streamManager) { try { await this.streamManager.publishActivity(fullActivity); } catch (error) { console.error('Failed to stream activity:', error); } } // Run after hooks await this.runAfterHooks(fullActivity); return fullActivity; } /** * Log multiple activities at once */ async logActivities(activities) { const results = []; for (const activity of activities) { try { const logged = await this.logActivity(activity); results.push(logged); } catch (error) { console.error('Failed to log activity:', error); } } return results; } /** * Subscribe to real-time activity updates */ subscribe(filter, callback) { const subscriptionId = (0, uuid_1.v4)(); const subscription = { id: subscriptionId, agentDID: filter.agentDID, parentDID: filter.parentDID, types: filter.types, callback, unsubscribe: () => { this.subscriptions.delete(subscriptionId); } }; this.subscriptions.set(subscriptionId, subscription); return subscription; } /** * Register a hook for activities */ registerHook(hook) { const hooks = this.hooks.get(hook.type) || []; hooks.push(hook); this.hooks.set(hook.type, hooks); } /** * Force process the current batch */ async flush() { if (this.activityBuffer.length > 0) { await this.processBatch(); } } /** * Process a batch of activities */ async processBatch() { if (this.processingBatch || this.activityBuffer.length === 0) { return; } this.processingBatch = true; const activities = [...this.activityBuffer]; this.activityBuffer = []; try { const batch = { id: (0, uuid_1.v4)(), activities, startTime: activities[0].timestamp, endTime: activities[activities.length - 1].timestamp, count: activities.length, agentDID: activities[0].agentDID, parentDID: activities[0].parentDID }; // Store to IPFS if enabled if (this.config.enableIPFS) { try { if (this.redundancyManager) { // Store with redundancy const result = await this.redundancyManager.storeBatchWithRedundancy(batch); if (result.success) { const successfulNode = result.nodes.find(n => n.ipfsHash); batch.batchHash = successfulNode?.ipfsHash; console.log(`Stored batch with redundancy: ${result.nodes.filter(n => n.ipfsHash).length}/${result.nodes.length} nodes`); } else { throw new Error('Failed to meet minimum replica requirement'); } } else if (this.ipfsStorage) { // Store to single node const storedBatch = await this.ipfsStorage.storeActivityBatch(batch); console.log(`Stored activity batch to IPFS: ${storedBatch.ipfsHash}`); batch.batchHash = storedBatch.ipfsHash; } } catch (ipfsError) { console.error('Failed to store batch to IPFS:', ipfsError); // Continue processing even if IPFS storage fails } } else { console.log(`Processing activity batch: ${batch.count} activities`); } // Stream batch processed event if (this.streamManager) { try { await this.streamManager.publishBatch(batch); } catch (error) { console.error('Failed to stream batch:', error); } } // Emit batch processed event await this.emitBatchProcessed(batch); } catch (error) { console.error('Failed to process batch:', error); // Re-add activities to buffer for retry this.activityBuffer.unshift(...activities); } finally { this.processingBatch = false; } } /** * Start the batch processing timer */ startBatchTimer() { this.batchTimer = setInterval(async () => { await this.processBatch(); }, this.config.batchInterval); } /** * Stop the batch processing timer */ stopBatchTimer() { if (this.batchTimer) { clearInterval(this.batchTimer); this.batchTimer = undefined; } } /** * Notify subscribers of a new activity */ async notifySubscribers(activity) { const promises = Array.from(this.subscriptions.values()) .filter(sub => this.matchesSubscription(activity, sub)) .map(sub => { try { return Promise.resolve(sub.callback(activity)); } catch (error) { console.error('Subscriber callback error:', error); return Promise.resolve(); } }); await Promise.all(promises); } /** * Check if an activity matches a subscription filter */ matchesSubscription(activity, subscription) { if (subscription.agentDID && activity.agentDID !== subscription.agentDID) { return false; } if (subscription.parentDID && activity.parentDID !== subscription.parentDID) { return false; } if (subscription.types && !subscription.types.includes(activity.type)) { return false; } return true; } /** * Run before hooks for an activity */ async runBeforeHooks(activity) { const hooks = this.hooks.get(activity.type) || []; for (const hook of hooks) { if (hook.beforeActivity) { const shouldContinue = await hook.beforeActivity(activity); if (!shouldContinue) { return false; } } } return true; } /** * Run after hooks for an activity */ async runAfterHooks(activity) { const hooks = this.hooks.get(activity.type) || []; const promises = hooks .filter(hook => hook.afterActivity) .map(hook => hook.afterActivity(activity)); await Promise.all(promises); } /** * Emit batch processed event */ async emitBatchProcessed(batch) { // This will be extended in future phases // For now, just log it console.log(`Batch processed: ${batch.id}, Activities: ${batch.count}`); } /** * Get current buffer size */ getBufferSize() { return this.activityBuffer.length; } /** * Get subscription count */ getSubscriptionCount() { return this.subscriptions.size; } /** * Retrieve activities from IPFS */ async retrieveActivitiesFromIPFS(ipfsHashes) { if (!this.ipfsStorage) { throw new Error('IPFS storage not enabled'); } const activities = []; for (const hash of ipfsHashes) { try { const batch = await this.ipfsStorage.retrieveActivityBatch(hash); activities.push(...batch.activities); } catch (error) { console.error(`Failed to retrieve batch ${hash}:`, error); } } return activities; } /** * Get IPFS storage instance */ getIPFSStorage() { return this.ipfsStorage; } /** * Get activity index instance */ getActivityIndex() { return this.activityIndex; } /** * Get stream manager instance */ getStreamManager() { return this.streamManager; } /** * Enable or disable IPFS storage */ async setIPFSEnabled(enabled, ipfsUrl) { this.config.enableIPFS = enabled; if (enabled && !this.ipfsStorage) { this.ipfsStorage = new ipfs_activity_storage_1.IPFSActivityStorage({ url: ipfsUrl || this.config.ipfsUrl, encryptionKey: this.config.encryptionKey }); } else if (!enabled && this.ipfsStorage) { // Flush any pending activities before disabling await this.flush(); this.ipfsStorage = undefined; } } /** * Check if IPFS is connected */ async checkIPFSConnection() { if (!this.ipfsStorage) { return false; } return await this.ipfsStorage.isConnected(); } /** * Cleanup resources */ async cleanup() { // Stop batch timer this.stopBatchTimer(); // Process remaining activities await this.flush(); // Clear subscriptions this.subscriptions.clear(); // Clear hooks this.hooks.clear(); } } exports.ActivityLogger = ActivityLogger; // Singleton instance for global access let globalLogger = null; /** * Get or create the global activity logger instance */ function getActivityLogger(config) { if (!globalLogger) { globalLogger = new ActivityLogger(config); } return globalLogger; } /** * Helper function to create a standard activity object */ function createActivity(type, params) { return { type, agentDID: params.agentDID, parentDID: params.parentDID, serviceDID: params.serviceDID, status: params.status, scopes: params.scopes || [], details: params.details || {}, sessionId: params.sessionId }; } //# sourceMappingURL=activity-logger.js.map