317 lines
8.2 KiB
TypeScript
317 lines
8.2 KiB
TypeScript
/**
|
|
* 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 };
|
|
};
|
|
}
|
|
|
|
// ============ Command 相关 ============
|
|
|
|
export interface CommandInfo {
|
|
name: string;
|
|
description?: string;
|
|
agent?: string;
|
|
model?: string;
|
|
subtask?: boolean;
|
|
source: string;
|
|
hasTemplate: boolean;
|
|
}
|
|
|
|
export interface CommandSearchResult {
|
|
name: string;
|
|
description?: string;
|
|
source: string;
|
|
score: number;
|
|
}
|
|
|
|
export interface CommandExecuteResult {
|
|
prompt: string;
|
|
agent?: string;
|
|
model?: string;
|
|
subtask?: boolean;
|
|
}
|
|
|
|
export interface CommandListResponse {
|
|
commands: Array<{ name: string; description?: string; source: string }>;
|
|
stats: { total: number; bySource: Record<string, number> };
|
|
}
|
|
|
|
// ============ Command CRUD 相关 ============
|
|
|
|
export interface CreateCommandInput {
|
|
name: string;
|
|
description?: string;
|
|
template: string;
|
|
agent?: string;
|
|
model?: string;
|
|
subtask?: boolean;
|
|
scope: 'user' | 'project';
|
|
}
|
|
|
|
export interface UpdateCommandInput {
|
|
description?: string;
|
|
template?: string;
|
|
agent?: string;
|
|
model?: string;
|
|
subtask?: boolean;
|
|
}
|
|
|
|
export interface CommandContent {
|
|
name: string;
|
|
description?: string;
|
|
template: string;
|
|
agent?: string;
|
|
model?: string;
|
|
subtask?: boolean;
|
|
source: string;
|
|
sourcePath?: string;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Commands
|
|
// ============================================================================
|
|
|
|
async listCommands(): Promise<{ success: boolean; data: CommandListResponse }> {
|
|
return this.request('GET', '/api/commands');
|
|
}
|
|
|
|
async getCommand(name: string): Promise<{ success: boolean; data: CommandInfo }> {
|
|
return this.request('GET', `/api/commands/${encodeURIComponent(name)}`);
|
|
}
|
|
|
|
async executeCommand(
|
|
name: string,
|
|
args: string = ''
|
|
): Promise<{ success: boolean; data?: CommandExecuteResult; error?: string }> {
|
|
return this.request('POST', `/api/commands/${encodeURIComponent(name)}/execute`, {
|
|
arguments: args,
|
|
});
|
|
}
|
|
|
|
async searchCommands(
|
|
query: string,
|
|
limit: number = 10
|
|
): Promise<{ success: boolean; data: CommandSearchResult[] }> {
|
|
return this.request('POST', '/api/commands/search', { query, limit });
|
|
}
|
|
|
|
async reloadCommands(): Promise<{
|
|
success: boolean;
|
|
data: { message: string; stats: { total: number; bySource: Record<string, number> } };
|
|
}> {
|
|
return this.request('POST', '/api/commands/reload');
|
|
}
|
|
|
|
// ============================================================================
|
|
// Commands CRUD
|
|
// ============================================================================
|
|
|
|
async createCommand(
|
|
input: CreateCommandInput
|
|
): Promise<{ success: boolean; data?: { name: string; path: string }; error?: string }> {
|
|
return this.request('POST', '/api/commands', input);
|
|
}
|
|
|
|
async getCommandContent(
|
|
name: string
|
|
): Promise<{ success: boolean; data?: CommandContent; error?: string }> {
|
|
return this.request('GET', `/api/commands/${encodeURIComponent(name)}/content`);
|
|
}
|
|
|
|
async updateCommand(
|
|
name: string,
|
|
input: UpdateCommandInput
|
|
): Promise<{ success: boolean; data?: { name: string; path: string }; error?: string }> {
|
|
return this.request('PUT', `/api/commands/${encodeURIComponent(name)}`, input);
|
|
}
|
|
|
|
async deleteCommand(
|
|
name: string
|
|
): Promise<{ success: boolean; data?: { name: string; path: string }; error?: string }> {
|
|
return this.request('DELETE', `/api/commands/${encodeURIComponent(name)}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 创建 API Client
|
|
*/
|
|
export function createClient(config: ClientConfig): APIClient {
|
|
return new APIClient(config);
|
|
}
|