Files
ai-terminal-assistant/packages/core/src/checkpoint/lfs.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

190 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Git LFS 大文件支持模块
* 参考 Cline 的 LFS 模式检测
*/
import * as fs from 'fs/promises';
import * as path from 'path';
import { minimatch } from 'minimatch';
/**
* LFS 模式加载器
* 从 .gitattributes 文件中加载 LFS 模式
*/
export class LFSPatternLoader {
private patterns: string[] = [];
private loaded = false;
/**
* 从工作目录加载 LFS 模式
*/
async loadPatterns(workDir: string): Promise<void> {
const gitattributesPath = path.join(workDir, '.gitattributes');
try {
const content = await fs.readFile(gitattributesPath, 'utf-8');
this.patterns = this.parseLfsPatterns(content);
this.loaded = true;
} catch {
// .gitattributes 不存在或无法读取
this.patterns = [];
this.loaded = true;
}
}
/**
* 解析 .gitattributes 内容,提取 LFS 模式
*/
private parseLfsPatterns(content: string): string[] {
const patterns: string[] = [];
for (const line of content.split('\n')) {
const trimmed = line.trim();
// 跳过空行和注释
if (!trimmed || trimmed.startsWith('#')) {
continue;
}
// 匹配 LFS 配置行: pattern filter=lfs diff=lfs merge=lfs -text
// 或简单形式: pattern filter=lfs
if (trimmed.includes('filter=lfs')) {
// 提取模式(第一个空白字符前的部分)
const match = trimmed.match(/^(\S+)/);
if (match) {
patterns.push(match[1]);
}
}
}
return patterns;
}
/**
* 检查文件是否为 LFS 管理的文件
*/
isLfsFile(filePath: string): boolean {
if (!this.loaded) {
return false;
}
// 规范化路径(使用正斜杠)
const normalizedPath = filePath.replace(/\\/g, '/');
return this.patterns.some((pattern) =>
minimatch(normalizedPath, pattern, { matchBase: true })
);
}
/**
* 获取所有 LFS 模式
*/
getPatterns(): string[] {
return [...this.patterns];
}
/**
* 获取排除模式(用于 .gitignore
*/
getExcludePatterns(): string[] {
return this.patterns.map((p) => {
// 确保模式以 / 开头(相对于仓库根目录)
if (!p.startsWith('/') && !p.startsWith('*')) {
return `/${p}`;
}
return p;
});
}
/**
* 检查是否已加载
*/
isLoaded(): boolean {
return this.loaded;
}
/**
* 重置(清除已加载的模式)
*/
reset(): void {
this.patterns = [];
this.loaded = false;
}
/**
* 过滤文件列表,排除 LFS 文件
*/
filterNonLfsFiles(files: string[]): string[] {
return files.filter((file) => !this.isLfsFile(file));
}
/**
* 获取 LFS 文件列表
*/
filterLfsFiles(files: string[]): string[] {
return files.filter((file) => this.isLfsFile(file));
}
}
/**
* 常见的大文件扩展名(作为 LFS 候选)
*/
export const COMMON_LARGE_FILE_EXTENSIONS = [
// 图片
'.psd',
'.ai',
'.eps',
'.tiff',
'.raw',
'.cr2',
'.nef',
// 视频
'.mp4',
'.mov',
'.avi',
'.mkv',
'.wmv',
'.flv',
// 音频
'.mp3',
'.wav',
'.flac',
'.aac',
'.ogg',
// 压缩文件
'.zip',
'.tar',
'.gz',
'.rar',
'.7z',
// 二进制
'.exe',
'.dll',
'.so',
'.dylib',
// 数据文件
'.db',
'.sqlite',
'.mdb',
// 其他
'.pdf',
'.docx',
'.xlsx',
'.pptx',
];
/**
* 检查文件扩展名是否为常见大文件类型
*/
export function isCommonLargeFile(filePath: string): boolean {
const ext = path.extname(filePath).toLowerCase();
return COMMON_LARGE_FILE_EXTENSIONS.includes(ext);
}
/**
* 创建 LFS 模式加载器实例
*/
export function createLFSPatternLoader(): LFSPatternLoader {
return new LFSPatternLoader();
}