Files
ai-terminal-assistant/packages/core/src/checkpoint/session-tracker.ts
T
kurihada cb554c65b4 feat(checkpoint): 添加 Checkpoint 可视化管理功能
Core 层增强:
- 添加 safety.ts: 7点安全检查机制
- 添加 session-tracker.ts: 会话级检查点跟踪
- 添加 lock.ts: 并发控制文件锁
- 添加 lfs.ts: Git LFS 大文件支持
- 添加 path-validator.ts: 路径验证
- 添加 commit-message.ts: 智能提交消息生成
- 增强 manager.ts: 支持三种恢复模式、unrevert 撤销回滚

Server 层:
- 添加 checkpoints.ts: 16个 REST API 端点
  - GET/POST /checkpoints: 列表/创建检查点
  - GET/DELETE /checkpoints/🆔 获取/删除检查点
  - GET /checkpoints/:id/diff: 获取差异
  - POST /checkpoints/:id/restore: 恢复到检查点
  - POST /checkpoints/unrevert: 撤销回滚
  - GET /checkpoints/:id/safety-check: 安全检查

UI 层:
- 添加 CheckpointPanel.tsx: 检查点列表面板
- 添加 CheckpointDiffViewer.tsx: 差异查看器
- 添加 RestoreDialog.tsx: 恢复确认对话框
- 添加 16 个 API 客户端函数
- 添加完整的 TypeScript 类型定义

Web/Desktop 集成:
- 添加 History 按钮到工具栏
- 集成 CheckpointPanel 组件
2025-12-12 22:52:27 +08:00

223 lines
5.0 KiB
TypeScript

/**
* 会话级检查点跟踪
* 参考 Aider 的 aider_commit_hashes 和 commit_before_message 机制
*/
import { nanoid } from 'nanoid';
import type { CheckpointManager } from './manager.js';
import type {
SessionState,
SessionStats,
CheckpointMetadata,
RollbackResult,
} from './types.js';
/**
* 会话跟踪器
* 跟踪当前会话的所有检查点和文件修改
*/
export class SessionTracker {
private currentSession: SessionState | null = null;
private checkpointManager: CheckpointManager;
constructor(checkpointManager: CheckpointManager) {
this.checkpointManager = checkpointManager;
}
/**
* 开始新会话
*/
async startSession(): Promise<string> {
// 创建会话开始检查点
const startCp = await this.checkpointManager.createCheckpoint({
trigger: 'session_start',
description: 'Session start',
});
const sessionId = nanoid(10);
this.currentSession = {
id: sessionId,
startTime: Date.now(),
startCheckpoint: startCp.id,
checkpoints: [startCp.id],
modifiedFiles: [],
};
return sessionId;
}
/**
* 结束当前会话
*/
async endSession(): Promise<void> {
if (!this.currentSession) return;
// 创建会话结束检查点
await this.checkpointManager.createCheckpoint({
trigger: 'session_end',
description: 'Session end',
});
this.currentSession = null;
}
/**
* 获取当前会话 ID
*/
getCurrentSessionId(): string | null {
return this.currentSession?.id ?? null;
}
/**
* 检查是否有活跃会话
*/
hasActiveSession(): boolean {
return this.currentSession !== null;
}
/**
* 记录检查点到当前会话
*/
recordCheckpoint(checkpointId: string): void {
if (this.currentSession) {
this.currentSession.checkpoints.push(checkpointId);
}
}
/**
* 记录文件修改
*/
recordFileChange(filePath: string): void {
if (this.currentSession) {
if (!this.currentSession.modifiedFiles.includes(filePath)) {
this.currentSession.modifiedFiles.push(filePath);
}
}
}
/**
* 记录多个文件修改
*/
recordFileChanges(filePaths: string[]): void {
for (const filePath of filePaths) {
this.recordFileChange(filePath);
}
}
/**
* 获取会话中修改的所有文件
*/
getModifiedFiles(): string[] {
return this.currentSession?.modifiedFiles ?? [];
}
/**
* 获取会话中的所有检查点
*/
async getSessionCheckpoints(): Promise<CheckpointMetadata[]> {
if (!this.currentSession) {
return [];
}
const checkpoints: CheckpointMetadata[] = [];
for (const id of this.currentSession.checkpoints) {
const cp = await this.checkpointManager.getCheckpoint(id);
if (cp) {
checkpoints.push(cp);
}
}
return checkpoints.sort((a, b) => a.timestamp - b.timestamp);
}
/**
* 撤销当前会话的所有修改
*/
async undoSession(): Promise<RollbackResult> {
if (!this.currentSession) {
throw new Error('No active session');
}
return this.checkpointManager.rollback({
target: this.currentSession.startCheckpoint,
skipSafetyCheck: true, // 会话级撤销跳过安全检查
});
}
/**
* 回滚到会话中的指定检查点
*/
async rollbackToCheckpoint(checkpointId: string): Promise<RollbackResult> {
if (!this.currentSession) {
throw new Error('No active session');
}
// 验证检查点属于当前会话
if (!this.currentSession.checkpoints.includes(checkpointId)) {
throw new Error('Checkpoint does not belong to current session');
}
return this.checkpointManager.rollback({
target: checkpointId,
});
}
/**
* 获取会话统计信息
*/
getSessionStats(): SessionStats {
if (!this.currentSession) {
throw new Error('No active session');
}
return {
duration: Date.now() - this.currentSession.startTime,
checkpointCount: this.currentSession.checkpoints.length,
modifiedFilesCount: this.currentSession.modifiedFiles.length,
modifiedFiles: [...this.currentSession.modifiedFiles],
};
}
/**
* 获取当前会话状态
*/
getSessionState(): SessionState | null {
if (!this.currentSession) {
return null;
}
return {
...this.currentSession,
modifiedFiles: [...this.currentSession.modifiedFiles],
checkpoints: [...this.currentSession.checkpoints],
};
}
/**
* 获取会话开始时的检查点
*/
getStartCheckpoint(): string | null {
return this.currentSession?.startCheckpoint ?? null;
}
/**
* 获取会话持续时间(毫秒)
*/
getSessionDuration(): number {
if (!this.currentSession) {
return 0;
}
return Date.now() - this.currentSession.startTime;
}
}
/**
* 创建会话跟踪器实例
*/
export function createSessionTracker(
checkpointManager: CheckpointManager
): SessionTracker {
return new SessionTracker(checkpointManager);
}