agentic-qe
Version:
Agentic Quality Engineering Fleet System - AI-driven quality management platform
227 lines • 9.06 kB
JavaScript
;
/**
* Agent Restart Command
*
* Gracefully restarts an agent while preserving configuration and state.
* Handles cleanup, state persistence, and re-initialization.
*
* @module cli/commands/agent/restart
*/
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;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AgentRestartCommand = void 0;
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const AgentRegistry_1 = require("../../../mcp/services/AgentRegistry");
const Logger_1 = require("../../../utils/Logger");
const logger = Logger_1.Logger.getInstance();
/**
* Agent Restart Command Implementation
*/
class AgentRestartCommand {
/**
* Execute agent restart
*
* @param options - Restart options
* @returns Restart result
*/
static async execute(options) {
const startTime = Date.now();
const { agentId, preserveState = true, timeout = this.DEFAULT_TIMEOUT, force = false } = options;
logger.info(`Restarting agent: ${agentId}`, { preserveState, timeout, force });
try {
// Get agent registry
const registry = (0, AgentRegistry_1.getAgentRegistry)();
// Verify agent exists
const registeredAgent = registry.getRegisteredAgent(agentId);
if (!registeredAgent) {
throw new Error(`Agent not found: ${agentId}`);
}
// Read current configuration
const agentConfigPath = path.join(this.AGENT_DIR, `${agentId}.json`);
const agentConfig = await this.readAgentConfig(agentConfigPath);
// Preserve state if requested
let savedState = null;
if (preserveState) {
savedState = await this.saveAgentState(agentId, registeredAgent);
}
// Mark agent as restarting
await this.updateAgentStatus(agentConfigPath, 'restarting');
// Terminate old instance
await this.terminateAgent(agentId, force, timeout);
// Wait for cleanup
await this.waitForCleanup(500);
// Spawn new instance with preserved config
const newAgent = await this.spawnNewInstance(registeredAgent.mcpType, agentConfig, savedState);
// Update configuration with new instance ID
await this.updateAgentConfig(agentConfigPath, {
...agentConfig,
status: 'active',
lastRestart: new Date().toISOString(),
restartCount: (agentConfig.restartCount || 0) + 1,
instanceId: newAgent.id
});
const restartTime = Date.now() - startTime;
logger.info(`Agent restarted successfully: ${agentId} -> ${newAgent.id}`, {
restartTime,
stateRestored: preserveState
});
return {
agentId,
oldInstanceId: registeredAgent.id,
newInstanceId: newAgent.id,
status: 'active',
preservedConfig: agentConfig,
restartTime,
stateRestored: preserveState && savedState !== null
};
}
catch (error) {
logger.error(`Failed to restart agent ${agentId}:`, error);
// Attempt to restore from backup
try {
await this.restoreFromBackup(agentId);
}
catch (restoreError) {
logger.error('Failed to restore from backup:', restoreError);
}
throw new Error(`Agent restart failed: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Read agent configuration
*/
static async readAgentConfig(configPath) {
if (!await fs.pathExists(configPath)) {
throw new Error(`Agent configuration not found: ${configPath}`);
}
return await fs.readJson(configPath);
}
/**
* Save agent state before restart
*/
static async saveAgentState(agentId, agent) {
const statePath = path.join(this.STATE_DIR, `${agentId}.state.json`);
await fs.ensureDir(this.STATE_DIR);
const state = {
savedAt: new Date().toISOString(),
agentId,
type: agent.type,
status: agent.status,
tasksCompleted: agent.tasksCompleted,
totalExecutionTime: agent.totalExecutionTime,
lastActivity: agent.lastActivity,
metadata: {
spawnedAt: agent.spawnedAt,
capabilities: agent.agent?.config?.capabilities || []
}
};
await fs.writeJson(statePath, state, { spaces: 2 });
logger.debug(`Agent state saved: ${statePath}`);
return state;
}
/**
* Update agent status in config
*/
static async updateAgentStatus(configPath, status) {
const config = await fs.readJson(configPath);
config.status = status;
config.statusUpdatedAt = new Date().toISOString();
await fs.writeJson(configPath, config, { spaces: 2 });
}
/**
* Update agent configuration
*/
static async updateAgentConfig(configPath, config) {
await fs.writeJson(configPath, config, { spaces: 2 });
}
/**
* Terminate agent with timeout
*/
static async terminateAgent(agentId, force, timeout) {
const registry = (0, AgentRegistry_1.getAgentRegistry)();
if (force) {
// Force termination
await registry.terminateAgent(agentId);
logger.debug(`Agent force terminated: ${agentId}`);
}
else {
// Graceful termination with timeout
const terminatePromise = registry.terminateAgent(agentId);
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Termination timeout')), timeout));
try {
await Promise.race([terminatePromise, timeoutPromise]);
logger.debug(`Agent gracefully terminated: ${agentId}`);
}
catch (error) {
// Timeout occurred, force terminate
logger.warn(`Graceful termination timeout, forcing: ${agentId}`);
await registry.terminateAgent(agentId);
}
}
}
/**
* Wait for cleanup to complete
*/
static async waitForCleanup(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* Spawn new agent instance
*/
static async spawnNewInstance(mcpType, config, savedState) {
const registry = (0, AgentRegistry_1.getAgentRegistry)();
const spawnConfig = {
name: config.name || `${mcpType}-${Date.now()}`,
description: config.description,
capabilities: config.capabilities || [],
resources: config.resources
};
const newAgent = await registry.spawnAgent(mcpType, spawnConfig);
// Restore state if available
if (savedState) {
logger.debug('Restoring agent state after restart');
// State restoration would happen here via agent methods
// This is a placeholder for state restoration logic
}
return newAgent;
}
/**
* Restore from backup
*/
static async restoreFromBackup(agentId) {
const backupPath = path.join(this.AGENT_DIR, `${agentId}.backup.json`);
const configPath = path.join(this.AGENT_DIR, `${agentId}.json`);
if (await fs.pathExists(backupPath)) {
await fs.copy(backupPath, configPath);
logger.info(`Restored agent config from backup: ${agentId}`);
}
}
}
exports.AgentRestartCommand = AgentRestartCommand;
AgentRestartCommand.AGENT_DIR = path.join(process.cwd(), '.aqe', 'agents');
AgentRestartCommand.STATE_DIR = path.join(process.cwd(), '.aqe', 'state');
AgentRestartCommand.DEFAULT_TIMEOUT = 30000;
//# sourceMappingURL=restart.js.map