feat: 重构为 Monorepo 架构并实现 HTTP Server
架构变更: - 采用 pnpm workspaces 实现 Monorepo 结构 - 将现有代码迁移到 packages/core - 新增 packages/server HTTP 服务层 Server 功能: - REST API: 会话管理、工具管理、配置管理 - WebSocket: 实时双向通信支持 - SSE: 服务端事件推送 - Hono + Bun 作为运行时 API 端点: - GET/POST /api/sessions - 会话 CRUD - GET/POST /api/sessions/:id/messages - 消息管理 - GET /api/sessions/:id/events - SSE 事件流 - WS /api/ws/:sessionId - WebSocket 连接 - GET/POST /api/tools - 工具管理 - GET/PUT /api/config - 配置管理
This commit is contained in:
@@ -0,0 +1,329 @@
|
||||
/**
|
||||
* Git 管理器
|
||||
*
|
||||
* 整合 GitRepo、AutoCommitManager、MessageGenerator、UndoManager
|
||||
* 提供统一的 Git 集成接口
|
||||
*/
|
||||
|
||||
import type {
|
||||
GitConfig,
|
||||
GitStatus,
|
||||
CommitResult,
|
||||
UndoResult,
|
||||
UndoEntry,
|
||||
DiffResult,
|
||||
CommitInfo,
|
||||
GitEvent,
|
||||
GitEventListener,
|
||||
} from './types.js';
|
||||
import { DEFAULT_GIT_CONFIG } from './types.js';
|
||||
import { GitRepo } from './repo.js';
|
||||
import { AutoCommitManager } from './auto-commit.js';
|
||||
import { MessageGenerator } from './message-generator.js';
|
||||
import { UndoManager } from './undo-manager.js';
|
||||
|
||||
export class GitManager {
|
||||
private repo: GitRepo;
|
||||
private autoCommit: AutoCommitManager;
|
||||
private messageGenerator: MessageGenerator;
|
||||
private undoManager: UndoManager;
|
||||
private config: GitConfig;
|
||||
private workdir: string;
|
||||
|
||||
/** 事件监听器 */
|
||||
private eventListeners: GitEventListener[] = [];
|
||||
|
||||
/** 是否已初始化 */
|
||||
private initialized: boolean = false;
|
||||
|
||||
constructor(workdir: string, config?: Partial<GitConfig>) {
|
||||
this.workdir = workdir;
|
||||
this.config = { ...DEFAULT_GIT_CONFIG, ...config };
|
||||
|
||||
// 创建组件
|
||||
this.repo = new GitRepo(workdir, this.config);
|
||||
this.messageGenerator = new MessageGenerator(this.config.messageFormat);
|
||||
this.autoCommit = new AutoCommitManager(
|
||||
this.repo,
|
||||
this.config.autoCommit,
|
||||
this.messageGenerator
|
||||
);
|
||||
this.undoManager = new UndoManager(this.repo, this.config.undo);
|
||||
|
||||
// 设置自动提交回调
|
||||
this.autoCommit.setOnCommit((result) => {
|
||||
if (result.success && result.shortHash && result.message) {
|
||||
// 记录到 undo 历史
|
||||
const files = this.autoCommit.getPendingFiles();
|
||||
this.undoManager.recordCommit(
|
||||
result.hash!,
|
||||
result.shortHash,
|
||||
result.message,
|
||||
files
|
||||
);
|
||||
|
||||
// 发送事件
|
||||
this.emitEvent('commit', {
|
||||
hash: result.shortHash,
|
||||
message: result.message,
|
||||
auto: true,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化 Git 管理器
|
||||
*/
|
||||
async initialize(): Promise<boolean> {
|
||||
if (!this.config.enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const isRepo = await this.repo.initialize();
|
||||
this.initialized = isRepo;
|
||||
return isRepo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否已初始化
|
||||
*/
|
||||
isInitialized(): boolean {
|
||||
return this.initialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取仓库状态
|
||||
*/
|
||||
async getStatus(): Promise<GitStatus> {
|
||||
return this.repo.getStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件变更后调用(由工具执行器调用)
|
||||
*/
|
||||
async onFileChanged(
|
||||
filePath: string,
|
||||
changeType: 'create' | 'modify' | 'delete'
|
||||
): Promise<void> {
|
||||
if (!this.initialized || !this.config.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.autoCommit.onFileChanged(filePath, changeType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动提交
|
||||
*/
|
||||
async commit(options: {
|
||||
message?: string;
|
||||
files?: string[];
|
||||
all?: boolean;
|
||||
} = {}): Promise<CommitResult> {
|
||||
if (!this.initialized) {
|
||||
return { success: false, error: 'Git not initialized' };
|
||||
}
|
||||
|
||||
// 如果有待提交的文件,先处理
|
||||
if (this.autoCommit.hasPendingFiles()) {
|
||||
await this.autoCommit.flush();
|
||||
}
|
||||
|
||||
// 获取差异用于生成消息
|
||||
const diff = await this.repo.getDiff({ staged: options.all });
|
||||
const message = options.message || this.messageGenerator.generate(diff);
|
||||
|
||||
const result = await this.repo.commit({
|
||||
message,
|
||||
files: options.files,
|
||||
all: options.all,
|
||||
aiEdits: true,
|
||||
});
|
||||
|
||||
if (result.success && result.shortHash && result.message) {
|
||||
// 记录到 undo 历史
|
||||
const files = options.files || [];
|
||||
this.undoManager.recordCommit(
|
||||
result.hash!,
|
||||
result.shortHash,
|
||||
result.message,
|
||||
files
|
||||
);
|
||||
|
||||
// 发送事件
|
||||
this.emitEvent('commit', {
|
||||
hash: result.shortHash,
|
||||
message: result.message,
|
||||
auto: false,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤销上一次 AI 提交
|
||||
*/
|
||||
async undo(): Promise<UndoResult> {
|
||||
if (!this.initialized) {
|
||||
return { success: false, message: 'Git not initialized' };
|
||||
}
|
||||
|
||||
const result = await this.undoManager.undo();
|
||||
|
||||
if (result.success) {
|
||||
this.emitEvent('undo', {
|
||||
hash: result.commitHash,
|
||||
files: result.restoredFiles,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 undo 预览
|
||||
*/
|
||||
getUndoPreview(): UndoEntry | null {
|
||||
return this.undoManager.getUndoPreview();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 undo 历史
|
||||
*/
|
||||
getUndoHistory(): UndoEntry[] {
|
||||
return this.undoManager.getHistory();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否可以 undo
|
||||
*/
|
||||
async canUndo(): Promise<{ canUndo: boolean; reason?: string }> {
|
||||
return this.undoManager.canUndo();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取差异
|
||||
*/
|
||||
async getDiff(options: {
|
||||
staged?: boolean;
|
||||
file?: string;
|
||||
commit?: string;
|
||||
} = {}): Promise<DiffResult> {
|
||||
return this.repo.getDiff(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最近的提交
|
||||
*/
|
||||
async getRecentCommits(count: number = 10): Promise<CommitInfo[]> {
|
||||
return this.repo.getRecentCommits(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前分支
|
||||
*/
|
||||
async getCurrentBranch(): Promise<string> {
|
||||
return this.repo.getCurrentBranch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制刷新待提交
|
||||
*/
|
||||
async flushPendingCommits(): Promise<CommitResult | null> {
|
||||
return this.autoCommit.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待提交文件
|
||||
*/
|
||||
getPendingFiles(): string[] {
|
||||
return this.autoCommit.getPendingFiles();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加事件监听器
|
||||
*/
|
||||
addEventListener(listener: GitEventListener): void {
|
||||
this.eventListeners.push(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除事件监听器
|
||||
*/
|
||||
removeEventListener(listener: GitEventListener): void {
|
||||
const index = this.eventListeners.indexOf(listener);
|
||||
if (index !== -1) {
|
||||
this.eventListeners.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送事件
|
||||
*/
|
||||
private emitEvent(type: GitEvent['type'], data: unknown): void {
|
||||
const event: GitEvent = {
|
||||
type,
|
||||
timestamp: Date.now(),
|
||||
data,
|
||||
};
|
||||
|
||||
for (const listener of this.eventListeners) {
|
||||
try {
|
||||
listener(event);
|
||||
} catch (error) {
|
||||
console.error('Git event listener error:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
*/
|
||||
getConfig(): GitConfig {
|
||||
return { ...this.config };
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新配置
|
||||
*/
|
||||
updateConfig(config: Partial<GitConfig>): void {
|
||||
this.config = { ...this.config, ...config };
|
||||
}
|
||||
}
|
||||
|
||||
// 全局 Git 管理器实例
|
||||
let gitManager: GitManager | null = null;
|
||||
|
||||
/**
|
||||
* 获取全局 Git 管理器
|
||||
*/
|
||||
export function getGitManager(): GitManager | null {
|
||||
return gitManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化全局 Git 管理器
|
||||
*/
|
||||
export async function initGitManager(
|
||||
workdir: string,
|
||||
config?: Partial<GitConfig>
|
||||
): Promise<GitManager | null> {
|
||||
gitManager = new GitManager(workdir, config);
|
||||
const initialized = await gitManager.initialize();
|
||||
|
||||
if (!initialized) {
|
||||
gitManager = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
return gitManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置全局 Git 管理器
|
||||
*/
|
||||
export function resetGitManager(): void {
|
||||
gitManager = null;
|
||||
}
|
||||
Reference in New Issue
Block a user