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 组件
This commit is contained in:
@@ -0,0 +1,222 @@
|
||||
/**
|
||||
* 会话级检查点跟踪
|
||||
* 参考 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);
|
||||
}
|
||||
Reference in New Issue
Block a user