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:
2025-12-12 10:42:20 +08:00
parent 59dbed926e
commit 5e32375f0e
301 changed files with 3281 additions and 43 deletions
+83
View File
@@ -0,0 +1,83 @@
import type { ToolMetadata, ToolSearchResult } from './types.js';
/**
* 分词函数,支持中英文
*/
function tokenize(text: string): string[] {
return text
.toLowerCase()
.split(/[\s,,、_\-]+/)
.filter((t) => t.length > 0);
}
/**
* 计算工具与查询的匹配分数
*/
function calculateScore(queryTerms: string[], tool: ToolMetadata): number {
let score = 0;
const nameLower = tool.name.toLowerCase();
const descLower = tool.description.toLowerCase();
const keywordsLower = tool.keywords.map((k) => k.toLowerCase());
for (const term of queryTerms) {
// 名称精确匹配 (最高分)
if (nameLower === term) {
score += 10;
}
// 名称包含匹配
else if (nameLower.includes(term)) {
score += 5;
}
// 关键词精确匹配
if (keywordsLower.includes(term)) {
score += 8;
}
// 关键词包含匹配
else if (keywordsLower.some((k) => k.includes(term) || term.includes(k))) {
score += 3;
}
// 描述包含匹配
if (descLower.includes(term)) {
score += 2;
}
}
return score;
}
/**
* 搜索工具
* @param query 搜索查询
* @param allTools 所有工具的元数据
* @param limit 返回结果数量限制
* @returns 匹配的工具列表(按分数排序)
*/
export function searchTools(
query: string,
allTools: ToolMetadata[],
limit: number = 5
): ToolSearchResult[] {
const queryTerms = tokenize(query);
if (queryTerms.length === 0) {
return [];
}
const results = allTools
// 只搜索延迟加载的工具
.filter((tool) => tool.deferLoading)
.map((tool) => ({
name: tool.name,
description: tool.description,
category: tool.category,
score: calculateScore(queryTerms, tool),
}))
.filter((r) => r.score > 0)
.sort((a, b) => b.score - a.score)
.slice(0, limit);
return results;
}