feat: 完善 Server 层并添加 CLI 和 Web 前端

Server 层增强:
- 添加 Agent 适配层,支持动态加载 core 模块
- 实现 Token 认证机制,支持本地/远程模式
- WebSocket 集成 Agent 实时对话

CLI 模块 (packages/cli):
- serve 命令启动 HTTP Server
- attach 命令连接远程 Server
- API Client 封装

Web 前端 (packages/web):
- React 18 + Vite + Tailwind CSS
- 会话管理侧边栏
- WebSocket 实时聊天界面
- 流式消息显示
This commit is contained in:
2025-12-12 11:22:25 +08:00
parent 5e32375f0e
commit 168996a475
35 changed files with 4028 additions and 52 deletions
+190
View File
@@ -0,0 +1,190 @@
/**
* API Client
*
* 用于连接远程 Server 的客户端
*/
export interface ClientConfig {
/** 服务器地址 */
baseUrl: string;
/** 认证 token */
token?: string;
/** 请求超时 (ms) */
timeout?: number;
}
export interface Session {
id: string;
name?: string;
createdAt: string;
updatedAt: string;
status: string;
messageCount: number;
}
export interface Message {
id: string;
role: 'user' | 'assistant' | 'system';
content: string;
timestamp: string;
}
export interface HealthStatus {
status: string;
timestamp: string;
agent: {
coreAvailable: boolean;
};
auth: {
enabled: boolean;
tokenCount: number;
};
stats: {
sessions: number;
websocket: { connections: number };
sse: { connections: number };
};
}
/**
* API Client 类
*/
export class APIClient {
private baseUrl: string;
private token?: string;
private timeout: number;
constructor(config: ClientConfig) {
this.baseUrl = config.baseUrl.replace(/\/$/, '');
this.token = config.token;
this.timeout = config.timeout || 30000;
}
/**
* 发送 HTTP 请求
*/
private async request<T>(
method: string,
path: string,
body?: unknown
): Promise<T> {
const url = `${this.baseUrl}${path}`;
const headers: Record<string, string> = {
'Content-Type': 'application/json',
};
if (this.token) {
headers['Authorization'] = `Bearer ${this.token}`;
}
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
try {
const response = await fetch(url, {
method,
headers,
body: body ? JSON.stringify(body) : undefined,
signal: controller.signal,
});
clearTimeout(timeoutId);
if (!response.ok) {
const errorData = await response.json().catch(() => ({ error: response.statusText })) as { error?: string };
throw new Error(errorData.error || `HTTP ${response.status}`);
}
return response.json() as Promise<T>;
} catch (error) {
clearTimeout(timeoutId);
if (error instanceof Error && error.name === 'AbortError') {
throw new Error('Request timeout');
}
throw error;
}
}
// ============================================================================
// Health
// ============================================================================
async health(): Promise<HealthStatus> {
return this.request('GET', '/health');
}
// ============================================================================
// Sessions
// ============================================================================
async listSessions(): Promise<{ success: boolean; data: Session[] }> {
return this.request('GET', '/api/sessions');
}
async createSession(name?: string): Promise<{ success: boolean; data: Session }> {
return this.request('POST', '/api/sessions', { name });
}
async getSession(id: string): Promise<{ success: boolean; data: Session }> {
return this.request('GET', `/api/sessions/${id}`);
}
async deleteSession(id: string): Promise<{ success: boolean }> {
return this.request('DELETE', `/api/sessions/${id}`);
}
// ============================================================================
// Messages
// ============================================================================
async getMessages(sessionId: string): Promise<{ success: boolean; data: Message[] }> {
return this.request('GET', `/api/sessions/${sessionId}/messages`);
}
async sendMessage(
sessionId: string,
content: string
): Promise<{ success: boolean; data: Message }> {
return this.request('POST', `/api/sessions/${sessionId}/messages`, { content });
}
// ============================================================================
// WebSocket
// ============================================================================
/**
* 创建 WebSocket 连接
*/
connectWebSocket(sessionId: string): WebSocket {
const wsUrl = this.baseUrl
.replace(/^http/, 'ws')
.concat(`/api/ws/${sessionId}`);
const url = this.token
? `${wsUrl}?token=${encodeURIComponent(this.token)}`
: wsUrl;
return new WebSocket(url);
}
// ============================================================================
// SSE
// ============================================================================
/**
* 获取 SSE URL (用于外部 EventSource 连接)
*/
getSSEUrl(sessionId: string): string {
const sseUrl = `${this.baseUrl}/api/sessions/${sessionId}/events`;
return this.token
? `${sseUrl}?token=${encodeURIComponent(this.token)}`
: sseUrl;
}
}
/**
* 创建 API Client
*/
export function createClient(config: ClientConfig): APIClient {
return new APIClient(config);
}
+6
View File
@@ -0,0 +1,6 @@
/**
* Client Module
*/
export { APIClient, createClient } from './api.js';
export type { ClientConfig, Session, Message, HealthStatus } from './api.js';