recoverFromState.ts•6.38 kB
import { Tool } from '@modelcontextprotocol/sdk/types.js';
import { Logger } from '../utils/logger.js';
import { StateManager } from '../components/StateManager.js';
import { ProcessManager } from '../components/ProcessManager.js';
export const recoverFromStateSchema: Tool = {
name: 'recover_from_state',
description: '保存された状態から復旧を試行',
inputSchema: {
type: 'object',
properties: {
force: {
type: 'boolean',
description: '強制的に復旧を実行するかどうか(デフォルト: false)',
default: false
}
},
additionalProperties: false
}
};
export async function recoverFromState(args: { force?: boolean } = {}): Promise<string> {
const logger = Logger.getInstance();
const stateManager = StateManager.getInstance();
const processManager = ProcessManager.getInstance();
try {
logger.info('Starting recovery from saved state', { force: args.force });
// 復旧情報を取得
const recoveryInfo = await stateManager.getRecoveryInfo();
if (!recoveryInfo.canRecover && !args.force) {
return JSON.stringify({
success: false,
message: '復旧可能な状態が見つかりません。force=trueで強制実行できます。',
recoveryInfo: {
canRecover: false,
hasDevProcess: !!recoveryInfo.devProcess,
lastHealthy: recoveryInfo.lastHealthy,
consecutiveFailures: recoveryInfo.consecutiveFailures
}
});
}
let recoveryResults = {
devProcessRecovered: false,
projectContextRecovered: false,
warnings: [] as string[]
};
// 開発サーバープロセスの復旧を試行
if (recoveryInfo.devProcess) {
try {
logger.info('Attempting to recover dev process', {
pid: recoveryInfo.devProcess.pid,
directory: recoveryInfo.devProcess.directory
});
// 既存のプロセスが動作中かチェック
const currentProcess = processManager.getCurrentProcess();
if (currentProcess && currentProcess.status === 'running') {
recoveryResults.warnings.push('開発サーバーは既に動作中です');
} else {
// プロセスが存在するかチェック
const isProcessAlive = await checkProcessExists(recoveryInfo.devProcess.pid);
if (isProcessAlive) {
// プロセスが生きている場合は、ProcessManagerに状態を復元
logger.info('Found existing process, restoring to ProcessManager');
// 注意: 実際のプロセス復元はProcessManagerの実装に依存
recoveryResults.warnings.push('既存のプロセスが検出されましたが、完全な復元には制限があります');
} else {
// プロセスが死んでいる場合は新しく開始
logger.info('Dead process detected, starting new dev server');
const newProcess = await processManager.startDevServer(
recoveryInfo.devProcess.directory
);
if (newProcess.status === 'running') {
recoveryResults.devProcessRecovered = true;
logger.info('Dev server restarted successfully', {
newPid: newProcess.pid,
directory: newProcess.directory
});
}
}
}
} catch (error) {
logger.error('Failed to recover dev process', { error });
recoveryResults.warnings.push(`開発サーバーの復旧に失敗: ${error}`);
}
}
// プロジェクトコンテキストの復旧
if (recoveryInfo.projectContext) {
try {
logger.info('Recovering project context', {
currentDirectory: recoveryInfo.projectContext.currentDirectory,
projectCount: recoveryInfo.projectContext.detectedProjects.length
});
// ProjectContextManagerに状態を復元
// 注意: 実際の復元はProjectContextManagerの実装に依存
recoveryResults.projectContextRecovered = true;
} catch (error) {
logger.error('Failed to recover project context', { error });
recoveryResults.warnings.push(`プロジェクトコンテキストの復旧に失敗: ${error}`);
}
}
// 復旧成功の場合は、現在の状態を保存
if (recoveryResults.devProcessRecovered) {
const currentProcess = processManager.getCurrentProcess();
if (currentProcess) {
await stateManager.saveDevProcessState(currentProcess);
}
}
const message = recoveryResults.devProcessRecovered || recoveryResults.projectContextRecovered
? '状態の復旧が完了しました'
: '復旧可能な項目が見つかりませんでした';
logger.info('Recovery completed', { recoveryResults });
return JSON.stringify({
success: true,
message,
recovery: {
devProcessRecovered: recoveryResults.devProcessRecovered,
projectContextRecovered: recoveryResults.projectContextRecovered,
warnings: recoveryResults.warnings,
previousProcess: recoveryInfo.devProcess ? {
pid: recoveryInfo.devProcess.pid,
directory: recoveryInfo.devProcess.directory,
status: recoveryInfo.devProcess.status,
ports: recoveryInfo.devProcess.ports
} : null,
recoveryTimestamp: new Date().toISOString()
}
});
} catch (error) {
logger.error('Recovery failed', { error });
return JSON.stringify({
success: false,
message: `復旧処理に失敗しました: ${error}`,
error: String(error)
});
}
}
/**
* プロセスが存在するかチェック
*/
async function checkProcessExists(pid: number): Promise<boolean> {
try {
// プロセスが存在するかチェック(kill -0)
process.kill(pid, 0);
return true;
} catch (error) {
// ESRCH = プロセスが存在しない
if ((error as NodeJS.ErrnoException).code === 'ESRCH') {
return false;
}
// EPERM = 権限なし(プロセスは存在する)
if ((error as NodeJS.ErrnoException).code === 'EPERM') {
return true;
}
// その他のエラー
throw error;
}
}