4ca8c413a6
- API 客户端使用完整后端 URL (localhost:3000) - 添加 tauri-plugin-http 支持外部 HTTP 请求 - 配置 CSP 允许连接 localhost - 同步 useChat hook 修复 WebSocket 错误处理
186 lines
4.5 KiB
TypeScript
186 lines
4.5 KiB
TypeScript
/**
|
|
* API Client for Web
|
|
*/
|
|
|
|
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 };
|
|
};
|
|
}
|
|
|
|
// Tauri 应用需要完整的后端 URL
|
|
const API_BASE = 'http://localhost:3000/api';
|
|
|
|
async function request<T>(method: string, path: string, body?: unknown): Promise<T> {
|
|
const response = await fetch(`${API_BASE}${path}`, {
|
|
method,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: body ? JSON.stringify(body) : undefined,
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const error = await response.json().catch(() => ({ error: response.statusText }));
|
|
throw new Error(error.error || `HTTP ${response.status}`);
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
|
|
// Health
|
|
export async function getHealth(): Promise<HealthStatus> {
|
|
const response = await fetch('http://localhost:3000/health');
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP ${response.status}`);
|
|
}
|
|
return response.json();
|
|
}
|
|
|
|
// Sessions
|
|
export async function listSessions(): Promise<{ success: boolean; data: Session[] }> {
|
|
return request('GET', '/sessions');
|
|
}
|
|
|
|
export async function createSession(name?: string): Promise<{ success: boolean; data: Session }> {
|
|
return request('POST', '/sessions', { name });
|
|
}
|
|
|
|
export async function getSession(id: string): Promise<{ success: boolean; data: Session }> {
|
|
return request('GET', `/sessions/${id}`);
|
|
}
|
|
|
|
export async function deleteSession(id: string): Promise<{ success: boolean }> {
|
|
return request('DELETE', `/sessions/${id}`);
|
|
}
|
|
|
|
// Messages
|
|
export async function getMessages(sessionId: string): Promise<{ success: boolean; data: Message[] }> {
|
|
return request('GET', `/sessions/${sessionId}/messages`);
|
|
}
|
|
|
|
export async function sendMessage(
|
|
sessionId: string,
|
|
content: string
|
|
): Promise<{ success: boolean; data: Message }> {
|
|
return request('POST', `/sessions/${sessionId}/messages`, { content });
|
|
}
|
|
|
|
// WebSocket
|
|
export function createWebSocket(sessionId: string): WebSocket {
|
|
// Tauri 应用直接连接后端
|
|
return new WebSocket(`ws://localhost:3000/api/ws/${sessionId}`);
|
|
}
|
|
|
|
// Files
|
|
export interface FileInfo {
|
|
name: string;
|
|
path: string;
|
|
type: 'file' | 'directory';
|
|
size: number;
|
|
modified: string;
|
|
extension?: string;
|
|
}
|
|
|
|
export interface FileListResponse {
|
|
success: boolean;
|
|
data: {
|
|
path: string;
|
|
absolutePath: string;
|
|
parent: string | null;
|
|
files: FileInfo[];
|
|
};
|
|
}
|
|
|
|
export interface FileReadResponse {
|
|
success: boolean;
|
|
data: {
|
|
path: string;
|
|
name: string;
|
|
type: string;
|
|
size: number;
|
|
modified: string;
|
|
content: string;
|
|
encoding: 'utf-8' | 'base64';
|
|
};
|
|
}
|
|
|
|
export interface FileTreeNode {
|
|
name: string;
|
|
path: string;
|
|
type: 'file' | 'directory';
|
|
children?: FileTreeNode[];
|
|
}
|
|
|
|
export interface FileTreeResponse {
|
|
success: boolean;
|
|
data: {
|
|
path: string;
|
|
tree: FileTreeNode[];
|
|
};
|
|
}
|
|
|
|
export async function getWorkingDirectory(): Promise<{ success: boolean; data: { workingDirectory: string; separator: string } }> {
|
|
return request('GET', '/files');
|
|
}
|
|
|
|
export async function listFiles(path: string = '.', showHidden: boolean = false): Promise<FileListResponse> {
|
|
const params = new URLSearchParams({ path });
|
|
if (showHidden) params.set('hidden', 'true');
|
|
return request('GET', `/files/list?${params}`);
|
|
}
|
|
|
|
export async function readFile(path: string): Promise<FileReadResponse> {
|
|
return request('GET', `/files/read?path=${encodeURIComponent(path)}`);
|
|
}
|
|
|
|
export async function getFileTree(path: string = '.', depth: number = 3): Promise<FileTreeResponse> {
|
|
const params = new URLSearchParams({ path, depth: String(depth) });
|
|
return request('GET', `/files/tree?${params}`);
|
|
}
|
|
|
|
// Config
|
|
export interface ServerConfig {
|
|
model: string;
|
|
maxTokens: number;
|
|
temperature: number;
|
|
workdir: string;
|
|
allowedPaths: string[];
|
|
deniedPaths: string[];
|
|
}
|
|
|
|
export async function getConfig(): Promise<{ success: boolean; data: ServerConfig }> {
|
|
return request('GET', '/config');
|
|
}
|
|
|
|
export async function updateConfig(config: Partial<ServerConfig>): Promise<{ success: boolean; data: ServerConfig }> {
|
|
return request('PATCH', '/config', config);
|
|
}
|