Files
IC-Coder-Plugin/src/services/apiClient.ts
XiaoFeng ba75541dd6 feat: 实现后端通信层
- 新增 HTTP 客户端(src/services/apiClient.ts)
  - 实现对话创建、消息发送、对话中止等 API 调用
  - 支持用户答案提交和对话历史查询
  - 统一的错误处理和超时控制

- 新增 SSE 事件处理器(src/services/sseHandler.ts)
  - 实现 Server-Sent Events 流式数据解析
  - 支持 MessageChunk、ToolExecution、AskUser、Error 等事件类型
  - 使用 eventsource-parser 库处理 SSE 数据流
  - 提供事件回调机制,支持实时 UI 更新
2025-12-16 19:09:04 +08:00

155 lines
3.5 KiB
TypeScript

/**
* API 客户端
* 封装与后端的 HTTP 通信
*/
import * as https from 'https';
import * as http from 'http';
import { URL } from 'url';
import { getApiUrl, getConfig } from '../config/settings';
import type { ToolCallResult, AnswerRequest, ToolResultResponse, AnswerResponse } from '../types/api';
/**
* HTTP 请求选项
*/
interface RequestOptions {
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
headers?: Record<string, string>;
body?: unknown;
timeout?: number;
}
/**
* 发送 HTTP 请求
*/
async function request<T>(path: string, options: RequestOptions): Promise<T> {
const url = new URL(getApiUrl(path));
const { timeout } = getConfig();
const isHttps = url.protocol === 'https:';
const httpModule = isHttps ? https : http;
const requestOptions: http.RequestOptions = {
hostname: url.hostname,
port: url.port || (isHttps ? 443 : 80),
path: url.pathname + url.search,
method: options.method,
headers: {
'Content-Type': 'application/json',
...options.headers
},
timeout: options.timeout || timeout
};
return new Promise((resolve, reject) => {
const req = httpModule.request(requestOptions, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
try {
const json = JSON.parse(data);
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
resolve(json as T);
} else {
reject(new Error(json.error || json.message || `HTTP ${res.statusCode}`));
}
} catch (e) {
reject(new Error(`解析响应失败: ${data}`));
}
});
});
req.on('error', (error) => {
reject(error);
});
req.on('timeout', () => {
req.destroy();
reject(new Error('请求超时'));
});
if (options.body) {
req.write(JSON.stringify(options.body));
}
req.end();
});
}
/**
* 提交工具执行结果
* POST /api/tool/result
*/
export async function submitToolResult(result: ToolCallResult): Promise<ToolResultResponse> {
console.log(`[API] 提交工具结果: callId=${result.id}`);
return request<ToolResultResponse>('/api/tool/result', {
method: 'POST',
body: result
});
}
/**
* 提交用户回答
* POST /api/task/answer
*/
export async function submitAnswer(answer: AnswerRequest): Promise<AnswerResponse> {
console.log(`[API] 提交用户回答: askId=${answer.askId}`);
return request<AnswerResponse>('/api/task/answer', {
method: 'POST',
body: answer
});
}
/**
* 健康检查
* GET /api/dialog/health
*/
export async function healthCheck(): Promise<{ status: string }> {
return request<{ status: string }>('/api/dialog/health', {
method: 'GET',
timeout: 5000
});
}
/**
* 创建成功的工具结果
*/
export function createSuccessResult(id: number, text: string): ToolCallResult {
return {
jsonrpc: '2.0',
id,
result: {
content: [{ type: 'text', text }],
isError: false
}
};
}
/**
* 创建业务错误的工具结果(如编译失败)
*/
export function createBusinessErrorResult(id: number, errorMessage: string): ToolCallResult {
return {
jsonrpc: '2.0',
id,
result: {
content: [{ type: 'text', text: errorMessage }],
isError: true
}
};
}
/**
* 创建系统错误的工具结果
*/
export function createSystemErrorResult(id: number, code: number, message: string): ToolCallResult {
return {
jsonrpc: '2.0',
id,
error: { code, message }
};
}