UNPKG

agentic-qe

Version:

Agentic Quality Engineering Fleet System - AI-driven quality management platform

250 lines 9.54 kB
"use strict"; /** * Agent Detach Command * * Detaches from an active agent console session, cleaning up resources * and saving session data. Supports graceful detachment with statistics. * * @module cli/commands/agent/detach */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.AgentDetachCommand = void 0; const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); const attach_1 = require("./attach"); const Logger_1 = require("../../../utils/Logger"); const logger = Logger_1.Logger.getInstance(); /** * Agent Detach Command Implementation */ class AgentDetachCommand { /** * Execute agent detach * * @param options - Detach options * @returns Detach result */ static async execute(options) { const { agentId, saveSession = true, showStats = true, force = false } = options; logger.info(`Detaching from agent: ${agentId}`, { saveSession, force }); try { // Get active session const session = attach_1.AgentAttachCommand.getActiveSession(agentId); if (!session) { if (force) { logger.warn(`No active session found for agent: ${agentId}, force detaching`); return this.createForceDetachResult(agentId); } throw new Error(`Not attached to agent: ${agentId}`); } // Calculate session duration const duration = Date.now() - session.attachedAt.getTime(); // Stop monitoring await this.stopMonitoring(session); // Update session status session.status = 'detached'; // Display stats if requested if (showStats) { this.displaySessionStats(session, duration); } // Save session data let sessionSaved = false; if (saveSession) { await this.archiveSession(session, duration); sessionSaved = true; } // Clean up session metadata await this.cleanupSession(session); const result = { agentId, sessionId: session.sessionId, detachedAt: new Date(), duration, stats: { ...session.stats }, sessionSaved }; logger.info(`Detached from agent: ${agentId}`, { sessionId: session.sessionId, duration: `${(duration / 1000).toFixed(2)}s` }); return result; } catch (error) { logger.error(`Failed to detach from agent ${agentId}:`, error); if (force) { logger.warn('Force detaching despite error'); return this.createForceDetachResult(agentId); } throw new Error(`Agent detach failed: ${error instanceof Error ? error.message : String(error)}`); } } /** * Stop monitoring activities */ static async stopMonitoring(session) { // Clear all intervals const intervals = ['logInterval', 'metricsInterval', 'eventsInterval']; for (const intervalName of intervals) { const interval = session[intervalName]; if (interval) { clearInterval(interval); delete session[intervalName]; } } // Remove all event listeners session.events.removeAllListeners(); logger.debug(`Stopped monitoring for session: ${session.sessionId}`); } /** * Display session statistics */ static displaySessionStats(session, duration) { console.log('\n' + '='.repeat(60)); console.log('📊 Session Statistics'); console.log('='.repeat(60)); console.log(`Agent ID: ${session.agentId}`); console.log(`Session ID: ${session.sessionId}`); console.log(`Duration: ${(duration / 1000).toFixed(2)}s`); console.log(`Logs Received: ${session.stats.logsReceived}`); console.log(`Events Received: ${session.stats.eventsReceived}`); console.log(`Metrics Received: ${session.stats.metricsReceived}`); console.log('='.repeat(60) + '\n'); } /** * Archive session data */ static async archiveSession(session, duration) { await fs.ensureDir(this.ARCHIVE_DIR); const archivePath = path.join(this.ARCHIVE_DIR, `${session.sessionId}.json`); const archiveData = { sessionId: session.sessionId, agentId: session.agentId, attachedAt: session.attachedAt.toISOString(), detachedAt: new Date().toISOString(), duration, stats: session.stats, status: 'completed' }; await fs.writeJson(archivePath, archiveData, { spaces: 2 }); logger.debug(`Session archived: ${archivePath}`); } /** * Cleanup session metadata */ static async cleanupSession(session) { const sessionPath = path.join(this.SESSIONS_DIR, `${session.sessionId}.json`); if (await fs.pathExists(sessionPath)) { await fs.remove(sessionPath); logger.debug(`Session metadata cleaned: ${sessionPath}`); } // Remove from active sessions // This assumes access to the private activeSessions map // In production, would need proper API access } /** * Create force detach result */ static createForceDetachResult(agentId) { return { agentId, sessionId: 'unknown', detachedAt: new Date(), duration: 0, stats: { logsReceived: 0, eventsReceived: 0, metricsReceived: 0 }, sessionSaved: false }; } /** * Detach all active sessions */ static async detachAll(options) { const activeSessions = attach_1.AgentAttachCommand.getAllActiveSessions(); logger.info(`Detaching from all active sessions (${activeSessions.length})`); const results = []; for (const session of activeSessions) { try { const result = await this.execute({ agentId: session.agentId, saveSession: options.saveSession, showStats: options.showStats, force: false }); results.push(result); } catch (error) { logger.error(`Failed to detach from agent ${session.agentId}:`, error); } } return results; } /** * Get archived sessions */ static async getArchivedSessions(limit) { await fs.ensureDir(this.ARCHIVE_DIR); const files = await fs.readdir(this.ARCHIVE_DIR); const jsonFiles = files.filter(f => f.endsWith('.json')); const sessions = await Promise.all(jsonFiles.map(async (file) => { const filePath = path.join(this.ARCHIVE_DIR, file); return await fs.readJson(filePath); })); // Sort by detached time descending sessions.sort((a, b) => new Date(b.detachedAt).getTime() - new Date(a.detachedAt).getTime()); return limit ? sessions.slice(0, limit) : sessions; } /** * Clean up old archived sessions */ static async cleanupOldSessions(olderThanDays) { await fs.ensureDir(this.ARCHIVE_DIR); const files = await fs.readdir(this.ARCHIVE_DIR); const cutoffTime = Date.now() - (olderThanDays * 24 * 60 * 60 * 1000); let cleaned = 0; for (const file of files) { if (!file.endsWith('.json')) continue; const filePath = path.join(this.ARCHIVE_DIR, file); const session = await fs.readJson(filePath); const detachedAt = new Date(session.detachedAt).getTime(); if (detachedAt < cutoffTime) { await fs.remove(filePath); cleaned++; } } logger.info(`Cleaned up ${cleaned} old archived sessions`); return cleaned; } } exports.AgentDetachCommand = AgentDetachCommand; _a = AgentDetachCommand; AgentDetachCommand.SESSIONS_DIR = path.join(process.cwd(), '.aqe', 'sessions'); AgentDetachCommand.ARCHIVE_DIR = path.join(_a.SESSIONS_DIR, 'archive'); //# sourceMappingURL=detach.js.map