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,255 @@
|
||||
import type { ModelMessage } from 'ai';
|
||||
import type { SessionData, Todo, SessionSummary } from './types.js';
|
||||
import { SessionStorage, sessionStorage } from './storage.js';
|
||||
|
||||
/**
|
||||
* 会话管理器
|
||||
* 提供高级会话操作接口
|
||||
*/
|
||||
export class SessionManager {
|
||||
private storage: SessionStorage;
|
||||
private currentSession: SessionData | null = null;
|
||||
private autoSaveInterval: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
constructor(storage?: SessionStorage) {
|
||||
this.storage = storage || sessionStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化 - 尝试恢复或创建新会话
|
||||
*/
|
||||
async init(workdir: string): Promise<SessionData> {
|
||||
// 尝试加载当前会话
|
||||
const existing = await this.storage.loadCurrentSession();
|
||||
|
||||
if (existing && existing.workdir === workdir) {
|
||||
// 同一工作目录,恢复会话
|
||||
this.currentSession = existing;
|
||||
} else {
|
||||
// 不同目录或无会话,归档旧会话并创建新的
|
||||
if (existing) {
|
||||
await this.storage.archiveCurrentSession();
|
||||
}
|
||||
this.currentSession = this.createNewSession(workdir);
|
||||
await this.save();
|
||||
}
|
||||
|
||||
// 启动自动保存
|
||||
this.startAutoSave();
|
||||
|
||||
return this.currentSession;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新会话
|
||||
*/
|
||||
private createNewSession(workdir: string): SessionData {
|
||||
return {
|
||||
id: this.storage.generateSessionId(),
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
workdir,
|
||||
messages: [],
|
||||
discoveredTools: [],
|
||||
todos: [],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话
|
||||
*/
|
||||
getSession(): SessionData | null {
|
||||
return this.currentSession;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存当前会话
|
||||
*/
|
||||
async save(): Promise<void> {
|
||||
if (this.currentSession) {
|
||||
await this.storage.saveCurrentSession(this.currentSession);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加消息
|
||||
*/
|
||||
async addMessage(message: ModelMessage): Promise<void> {
|
||||
if (!this.currentSession) return;
|
||||
this.currentSession.messages.push(message);
|
||||
await this.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量设置消息(用于同步整个对话历史)
|
||||
*/
|
||||
async setMessages(messages: ModelMessage[]): Promise<void> {
|
||||
if (!this.currentSession) return;
|
||||
this.currentSession.messages = messages;
|
||||
await this.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对话历史
|
||||
*/
|
||||
getMessages(): ModelMessage[] {
|
||||
return this.currentSession?.messages || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置已发现的工具
|
||||
*/
|
||||
async setDiscoveredTools(tools: string[]): Promise<void> {
|
||||
if (!this.currentSession) return;
|
||||
this.currentSession.discoveredTools = tools;
|
||||
await this.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取已发现的工具
|
||||
*/
|
||||
getDiscoveredTools(): string[] {
|
||||
return this.currentSession?.discoveredTools || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新待办事项
|
||||
*/
|
||||
async setTodos(todos: Todo[]): Promise<void> {
|
||||
if (!this.currentSession) return;
|
||||
this.currentSession.todos = todos;
|
||||
await this.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待办事项
|
||||
*/
|
||||
getTodos(): Todo[] {
|
||||
return this.currentSession?.todos || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空当前会话并创建新会话
|
||||
*/
|
||||
async newSession(workdir?: string): Promise<SessionData> {
|
||||
// 归档当前会话
|
||||
if (this.currentSession && this.currentSession.messages.length > 0) {
|
||||
await this.storage.archiveCurrentSession();
|
||||
}
|
||||
|
||||
// 创建新会话
|
||||
const newWorkdir = workdir || this.currentSession?.workdir || process.cwd();
|
||||
this.currentSession = this.createNewSession(newWorkdir);
|
||||
await this.save();
|
||||
|
||||
return this.currentSession;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建子会话(用于 Task 工具)
|
||||
* @param parentId 父会话 ID
|
||||
* @param agentName 关联的 Agent 名称
|
||||
* @param title 会话标题
|
||||
*/
|
||||
createChildSession(parentId: string, agentName: string, title?: string): SessionData {
|
||||
const workdir = this.currentSession?.workdir || process.cwd();
|
||||
const childSession: SessionData = {
|
||||
id: this.storage.generateSessionId(),
|
||||
parentId,
|
||||
agentName,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
workdir,
|
||||
title: title || `子任务 (@${agentName})`,
|
||||
messages: [],
|
||||
discoveredTools: [],
|
||||
todos: [],
|
||||
};
|
||||
return childSession;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存子会话
|
||||
*/
|
||||
async saveChildSession(session: SessionData): Promise<void> {
|
||||
await this.storage.saveSession(session);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话 ID
|
||||
*/
|
||||
getSessionId(): string | undefined {
|
||||
return this.currentSession?.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复指定会话
|
||||
*/
|
||||
async restoreSession(sessionId: string): Promise<SessionData | null> {
|
||||
const session = await this.storage.loadSession(sessionId);
|
||||
if (!session) return null;
|
||||
|
||||
// 归档当前会话
|
||||
if (this.currentSession && this.currentSession.messages.length > 0) {
|
||||
await this.storage.archiveCurrentSession();
|
||||
}
|
||||
|
||||
this.currentSession = session;
|
||||
await this.save();
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* 列出历史会话
|
||||
*/
|
||||
async listSessions(): Promise<SessionSummary[]> {
|
||||
return this.storage.listSessions();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除历史会话
|
||||
*/
|
||||
async deleteSession(sessionId: string): Promise<boolean> {
|
||||
return this.storage.deleteSession(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动自动保存(每 30 秒)
|
||||
*/
|
||||
private startAutoSave(): void {
|
||||
if (this.autoSaveInterval) return;
|
||||
|
||||
this.autoSaveInterval = setInterval(async () => {
|
||||
await this.save();
|
||||
}, 30000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止自动保存
|
||||
*/
|
||||
stopAutoSave(): void {
|
||||
if (this.autoSaveInterval) {
|
||||
clearInterval(this.autoSaveInterval);
|
||||
this.autoSaveInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭管理器(保存并停止自动保存)
|
||||
*/
|
||||
async close(): Promise<void> {
|
||||
this.stopAutoSave();
|
||||
await this.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理旧会话
|
||||
*/
|
||||
async cleanup(keepCount?: number): Promise<number> {
|
||||
return this.storage.cleanupOldSessions(keepCount);
|
||||
}
|
||||
}
|
||||
|
||||
// 导出默认实例
|
||||
export const sessionManager = new SessionManager();
|
||||
Reference in New Issue
Block a user