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,109 @@
|
||||
/**
|
||||
* Config API Routes
|
||||
*
|
||||
* 配置管理相关的 REST API
|
||||
*/
|
||||
|
||||
import { Hono } from 'hono';
|
||||
|
||||
export const configRouter = new Hono();
|
||||
|
||||
// 服务器配置 (后续会从配置文件加载)
|
||||
interface ServerConfig {
|
||||
model: string;
|
||||
maxTokens: number;
|
||||
temperature: number;
|
||||
workdir: string;
|
||||
allowedPaths: string[];
|
||||
deniedPaths: string[];
|
||||
}
|
||||
|
||||
let serverConfig: ServerConfig = {
|
||||
model: 'claude-sonnet-4-20250514',
|
||||
maxTokens: 8192,
|
||||
temperature: 0.7,
|
||||
workdir: process.cwd(),
|
||||
allowedPaths: [],
|
||||
deniedPaths: [],
|
||||
};
|
||||
|
||||
/**
|
||||
* GET /config - 获取当前配置
|
||||
*/
|
||||
configRouter.get('/', (c) => {
|
||||
return c.json({
|
||||
success: true,
|
||||
data: serverConfig,
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* PUT /config - 更新配置
|
||||
*/
|
||||
configRouter.put('/', async (c) => {
|
||||
try {
|
||||
const body = await c.req.json();
|
||||
|
||||
// 合并配置 (只更新提供的字段)
|
||||
serverConfig = {
|
||||
...serverConfig,
|
||||
...body,
|
||||
};
|
||||
|
||||
return c.json({
|
||||
success: true,
|
||||
data: serverConfig,
|
||||
});
|
||||
} catch (error) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Invalid input',
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* PATCH /config - 部分更新配置
|
||||
*/
|
||||
configRouter.patch('/', async (c) => {
|
||||
try {
|
||||
const body = await c.req.json();
|
||||
|
||||
// 部分更新
|
||||
Object.keys(body).forEach((key) => {
|
||||
if (key in serverConfig) {
|
||||
(serverConfig as any)[key] = body[key];
|
||||
}
|
||||
});
|
||||
|
||||
return c.json({
|
||||
success: true,
|
||||
data: serverConfig,
|
||||
});
|
||||
} catch (error) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Invalid input',
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 获取当前配置 (内部使用)
|
||||
*/
|
||||
export function getConfig(): ServerConfig {
|
||||
return { ...serverConfig };
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置配置 (内部使用)
|
||||
*/
|
||||
export function setConfig(config: Partial<ServerConfig>): void {
|
||||
serverConfig = { ...serverConfig, ...config };
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* API Routes Index
|
||||
*
|
||||
* 聚合所有 API 路由
|
||||
*/
|
||||
|
||||
export { sessionsRouter } from './sessions.js';
|
||||
export { toolsRouter, registerTool, getRegisteredTools } from './tools.js';
|
||||
export { configRouter, getConfig, setConfig } from './config.js';
|
||||
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* Sessions API Routes
|
||||
*
|
||||
* 会话管理相关的 REST API
|
||||
*/
|
||||
|
||||
import { Hono } from 'hono';
|
||||
import { getSessionManager } from '../session/manager.js';
|
||||
import { CreateSessionInputSchema, SendMessageInputSchema } from '../types.js';
|
||||
|
||||
export const sessionsRouter = new Hono();
|
||||
|
||||
const sessionManager = getSessionManager();
|
||||
|
||||
/**
|
||||
* GET /sessions - 列出所有会话
|
||||
*/
|
||||
sessionsRouter.get('/', (c) => {
|
||||
const sessions = sessionManager.list();
|
||||
return c.json({
|
||||
success: true,
|
||||
data: sessions,
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /sessions - 创建新会话
|
||||
*/
|
||||
sessionsRouter.post('/', async (c) => {
|
||||
try {
|
||||
const body = await c.req.json();
|
||||
const input = CreateSessionInputSchema.parse(body);
|
||||
const session = sessionManager.create(input);
|
||||
|
||||
return c.json(
|
||||
{
|
||||
success: true,
|
||||
data: session,
|
||||
},
|
||||
201
|
||||
);
|
||||
} catch (error) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Invalid input',
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /sessions/:id - 获取单个会话
|
||||
*/
|
||||
sessionsRouter.get('/:id', (c) => {
|
||||
const id = c.req.param('id');
|
||||
const session = sessionManager.get(id);
|
||||
|
||||
if (!session) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'Session not found',
|
||||
},
|
||||
404
|
||||
);
|
||||
}
|
||||
|
||||
return c.json({
|
||||
success: true,
|
||||
data: session,
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* DELETE /sessions/:id - 删除会话
|
||||
*/
|
||||
sessionsRouter.delete('/:id', (c) => {
|
||||
const id = c.req.param('id');
|
||||
|
||||
if (!sessionManager.exists(id)) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'Session not found',
|
||||
},
|
||||
404
|
||||
);
|
||||
}
|
||||
|
||||
sessionManager.delete(id);
|
||||
|
||||
return c.json({
|
||||
success: true,
|
||||
message: 'Session deleted',
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /sessions/:id/messages - 获取会话消息
|
||||
*/
|
||||
sessionsRouter.get('/:id/messages', (c) => {
|
||||
const id = c.req.param('id');
|
||||
|
||||
if (!sessionManager.exists(id)) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'Session not found',
|
||||
},
|
||||
404
|
||||
);
|
||||
}
|
||||
|
||||
const messages = sessionManager.getMessages(id);
|
||||
|
||||
return c.json({
|
||||
success: true,
|
||||
data: messages,
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /sessions/:id/messages - 发送消息
|
||||
*
|
||||
* 注意: 这个端点仅用于添加消息记录。
|
||||
* 实际的 AI 对话应该通过 WebSocket 进行。
|
||||
*/
|
||||
sessionsRouter.post('/:id/messages', async (c) => {
|
||||
const id = c.req.param('id');
|
||||
|
||||
if (!sessionManager.exists(id)) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'Session not found',
|
||||
},
|
||||
404
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const body = await c.req.json();
|
||||
const input = SendMessageInputSchema.parse(body);
|
||||
|
||||
const message = sessionManager.addMessage(id, {
|
||||
role: input.role,
|
||||
content: input.content,
|
||||
});
|
||||
|
||||
if (!message) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'Failed to add message',
|
||||
},
|
||||
500
|
||||
);
|
||||
}
|
||||
|
||||
return c.json(
|
||||
{
|
||||
success: true,
|
||||
data: message,
|
||||
},
|
||||
201
|
||||
);
|
||||
} catch (error) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Invalid input',
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* Tools API Routes
|
||||
*
|
||||
* 工具管理相关的 REST API
|
||||
*/
|
||||
|
||||
import { Hono } from 'hono';
|
||||
import type { Tool } from '../types.js';
|
||||
|
||||
export const toolsRouter = new Hono();
|
||||
|
||||
// 工具注册表 (后续会从 core 模块获取)
|
||||
const toolRegistry: Map<string, Tool> = new Map();
|
||||
|
||||
/**
|
||||
* 注册工具 (内部使用)
|
||||
*/
|
||||
export function registerTool(tool: Tool): void {
|
||||
toolRegistry.set(tool.name, tool);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有已注册的工具
|
||||
*/
|
||||
export function getRegisteredTools(): Tool[] {
|
||||
return Array.from(toolRegistry.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /tools - 列出所有可用工具
|
||||
*/
|
||||
toolsRouter.get('/', (c) => {
|
||||
const tools = getRegisteredTools();
|
||||
|
||||
return c.json({
|
||||
success: true,
|
||||
data: tools,
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /tools/:name - 获取单个工具详情
|
||||
*/
|
||||
toolsRouter.get('/:name', (c) => {
|
||||
const name = c.req.param('name');
|
||||
const tool = toolRegistry.get(name);
|
||||
|
||||
if (!tool) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'Tool not found',
|
||||
},
|
||||
404
|
||||
);
|
||||
}
|
||||
|
||||
return c.json({
|
||||
success: true,
|
||||
data: tool,
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /tools/:name/execute - 执行工具
|
||||
*
|
||||
* 注意: 工具执行是同步的,对于长时间运行的工具,
|
||||
* 建议通过 WebSocket 执行以获取实时反馈。
|
||||
*/
|
||||
toolsRouter.post('/:name/execute', async (c) => {
|
||||
const name = c.req.param('name');
|
||||
const tool = toolRegistry.get(name);
|
||||
|
||||
if (!tool) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'Tool not found',
|
||||
},
|
||||
404
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const body = await c.req.json();
|
||||
const params = body.params || {};
|
||||
|
||||
// TODO: 实际调用 core 模块的工具执行逻辑
|
||||
// const result = await executeTool(name, params);
|
||||
|
||||
return c.json({
|
||||
success: true,
|
||||
data: {
|
||||
tool: name,
|
||||
params,
|
||||
result: null, // 占位,后续实现
|
||||
message: 'Tool execution not yet implemented',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Execution failed',
|
||||
},
|
||||
500
|
||||
);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user