250d2cb4b5
- 新增 CodeEditor 组件,基于 CodeMirror 实现多标签代码编辑 - 新增 FileExplorer 组件,支持文件树展开/折叠和文件选择 - 新增 IDE 组件,整合文件浏览器和代码编辑器 - 新增 SessionPanel 组件,用于会话管理 - 添加文件写入 API(PUT /api/files/write) - 优化布局:IDE 始终显示,移除文件切换按钮 - 工作目录路径显示在文件浏览器标题栏,支持悬浮显示完整路径
1116 lines
24 KiB
TypeScript
1116 lines
24 KiB
TypeScript
/**
|
||
* Configurable API Client
|
||
*/
|
||
|
||
import type {
|
||
Session,
|
||
Message,
|
||
HealthStatus,
|
||
FileListResponse,
|
||
FileReadResponse,
|
||
FileTreeResponse,
|
||
ServerConfig,
|
||
CommandInfo,
|
||
CommandSearchResult,
|
||
CommandExecuteResult,
|
||
CommandListResponse,
|
||
CreateCommandInput,
|
||
UpdateCommandInput,
|
||
CommandContent,
|
||
MCPServerStatus,
|
||
MCPToolInfo,
|
||
MCPConfig,
|
||
HookConfig,
|
||
FileHookConfig,
|
||
ShellCommandConfig,
|
||
HookTestResult,
|
||
AgentListItem,
|
||
AgentDetail,
|
||
AgentInput,
|
||
AgentDefaults,
|
||
CheckpointListItem,
|
||
CheckpointDetail,
|
||
CheckpointStats,
|
||
DiffInfo,
|
||
FileDiffDetail,
|
||
RestoreOptions,
|
||
RestoreResult,
|
||
SafetyCheckResult,
|
||
UnrevertStatus,
|
||
UnrevertResult,
|
||
// Provider types
|
||
ProviderListItem,
|
||
ProviderDetail,
|
||
ModelInfo,
|
||
CustomProviderDefinition,
|
||
ProviderConfig,
|
||
ConnectionTestResult,
|
||
// Context types
|
||
ContextUsageInfo,
|
||
CompressionResult,
|
||
// File search types
|
||
FileSearchResponse,
|
||
// LSP types
|
||
LSPServer,
|
||
DiagnosticsResponse,
|
||
} from './types.js';
|
||
|
||
// Re-export types
|
||
export type {
|
||
Session,
|
||
Message,
|
||
HealthStatus,
|
||
FileInfo,
|
||
FileListResponse,
|
||
FileReadResponse,
|
||
FileTreeNode,
|
||
FileTreeResponse,
|
||
ServerConfig,
|
||
CommandInfo,
|
||
CommandSearchResult,
|
||
CommandExecuteResult,
|
||
CommandListResponse,
|
||
CreateCommandInput,
|
||
UpdateCommandInput,
|
||
CommandContent,
|
||
MCPServerStatus,
|
||
MCPServerStatusType,
|
||
MCPToolInfo,
|
||
MCPConfig,
|
||
MCPServerConfigInfo,
|
||
// Hooks types
|
||
HookConfig,
|
||
FileHookConfig,
|
||
ShellCommandConfig,
|
||
HookTestResult,
|
||
// Agent types
|
||
AgentMode,
|
||
AgentModelConfig,
|
||
AgentToolConfig,
|
||
PermissionRule,
|
||
AgentBashPermission,
|
||
AgentFilePermission,
|
||
AgentGitPermission,
|
||
AgentPermission,
|
||
AgentListItem,
|
||
AgentDetail,
|
||
AgentInput,
|
||
AgentDefaults,
|
||
// Checkpoint types
|
||
CheckpointTrigger,
|
||
FileChangeType,
|
||
CheckpointListItem,
|
||
CheckpointDetail,
|
||
FileChange,
|
||
DiffInfo,
|
||
FileDiffDetail,
|
||
RestoreMode,
|
||
RestoreOptions,
|
||
RestoreResult,
|
||
SafetyCheckResult,
|
||
CheckpointStats,
|
||
UnrevertStatus,
|
||
UnrevertResult,
|
||
// Provider types
|
||
BuiltinProviderType,
|
||
ProviderType,
|
||
ModelCapabilities,
|
||
ModelInfo,
|
||
ProviderListItem,
|
||
ProviderDetail,
|
||
CustomProviderDefinition,
|
||
ProviderConfig,
|
||
ConnectionTestResult,
|
||
// Context compression types
|
||
TokenUsage,
|
||
ContextUsageInfo,
|
||
CompressionStatus,
|
||
CompressionType,
|
||
CompressionResult,
|
||
// WebSocket error types
|
||
ConfigErrorPayload,
|
||
// File search types
|
||
FileSearchResult,
|
||
FileSearchResponse,
|
||
// Agent mode types
|
||
AgentModeType,
|
||
// Question types (for ask_user_question tool)
|
||
QuestionOption,
|
||
Question,
|
||
QuestionMessagePart,
|
||
// LSP types
|
||
LSPServer,
|
||
FileDiagnostic,
|
||
DiagnosticsSummary,
|
||
SingleFileDiagnosticsResponse,
|
||
AllFilesDiagnosticsResponse,
|
||
DiagnosticsResponse,
|
||
} from './types.js';
|
||
|
||
// API Configuration
|
||
interface ApiConfig {
|
||
baseUrl: string;
|
||
wsBaseUrl: () => string;
|
||
healthUrl: () => string;
|
||
}
|
||
|
||
let apiConfig: ApiConfig = {
|
||
baseUrl: '/api',
|
||
wsBaseUrl: () => {
|
||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||
return `${protocol}//${window.location.host}/api`;
|
||
},
|
||
healthUrl: () => '/health',
|
||
};
|
||
|
||
/**
|
||
* Configure API client for different environments
|
||
*/
|
||
export function configureApiClient(config: {
|
||
baseUrl: string;
|
||
wsBaseUrl: string | (() => string);
|
||
healthUrl?: string | (() => string);
|
||
}) {
|
||
apiConfig = {
|
||
baseUrl: config.baseUrl,
|
||
wsBaseUrl:
|
||
typeof config.wsBaseUrl === 'function'
|
||
? config.wsBaseUrl
|
||
: () => config.wsBaseUrl as string,
|
||
healthUrl: config.healthUrl
|
||
? typeof config.healthUrl === 'function'
|
||
? config.healthUrl
|
||
: () => config.healthUrl as string
|
||
: () => '/health',
|
||
};
|
||
}
|
||
|
||
async function request<T>(method: string, path: string, body?: unknown): Promise<T> {
|
||
const response = await fetch(`${apiConfig.baseUrl}${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 healthUrl = apiConfig.healthUrl();
|
||
const response = await fetch(healthUrl);
|
||
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 {
|
||
const wsBase = apiConfig.wsBaseUrl();
|
||
return new WebSocket(`${wsBase}/ws/${sessionId}`);
|
||
}
|
||
|
||
// Files
|
||
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 interface FileWriteResponse {
|
||
success: boolean;
|
||
data: {
|
||
path: string;
|
||
name: string;
|
||
size: number;
|
||
modified: string;
|
||
};
|
||
}
|
||
|
||
export async function writeFile(path: string, content: string): Promise<FileWriteResponse> {
|
||
return request('PUT', '/files/write', { path, content });
|
||
}
|
||
|
||
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 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);
|
||
}
|
||
|
||
// Commands
|
||
export async function listCommands(): Promise<{ success: boolean; data: CommandListResponse }> {
|
||
return request('GET', '/commands');
|
||
}
|
||
|
||
export async function getCommand(name: string): Promise<{ success: boolean; data: CommandInfo }> {
|
||
return request('GET', `/commands/${encodeURIComponent(name)}`);
|
||
}
|
||
|
||
export async function executeCommand(
|
||
name: string,
|
||
args: string = ''
|
||
): Promise<{ success: boolean; data?: CommandExecuteResult; error?: string }> {
|
||
return request('POST', `/commands/${encodeURIComponent(name)}/execute`, {
|
||
arguments: args,
|
||
});
|
||
}
|
||
|
||
export async function searchCommands(
|
||
query: string,
|
||
limit: number = 10
|
||
): Promise<{ success: boolean; data: CommandSearchResult[] }> {
|
||
return request('POST', '/commands/search', { query, limit });
|
||
}
|
||
|
||
export async function reloadCommands(): Promise<{
|
||
success: boolean;
|
||
data: { message: string; stats: { total: number; bySource: Record<string, number> } };
|
||
}> {
|
||
return request('POST', '/commands/reload');
|
||
}
|
||
|
||
// Commands CRUD
|
||
export async function createCommand(
|
||
input: CreateCommandInput
|
||
): Promise<{ success: boolean; data?: { name: string; path: string }; error?: string }> {
|
||
return request('POST', '/commands', input);
|
||
}
|
||
|
||
export async function getCommandContent(
|
||
name: string
|
||
): Promise<{ success: boolean; data?: CommandContent; error?: string }> {
|
||
return request('GET', `/commands/${encodeURIComponent(name)}/content`);
|
||
}
|
||
|
||
export async function updateCommand(
|
||
name: string,
|
||
input: UpdateCommandInput
|
||
): Promise<{ success: boolean; data?: { name: string; path: string }; error?: string }> {
|
||
return request('PUT', `/commands/${encodeURIComponent(name)}`, input);
|
||
}
|
||
|
||
export async function deleteCommand(
|
||
name: string
|
||
): Promise<{ success: boolean; data?: { name: string; path: string }; error?: string }> {
|
||
return request('DELETE', `/commands/${encodeURIComponent(name)}`);
|
||
}
|
||
|
||
// ============ MCP API ============
|
||
|
||
/**
|
||
* 获取所有 MCP 服务器状态
|
||
*/
|
||
export async function listMCPServers(): Promise<{
|
||
success: boolean;
|
||
data: MCPServerStatus[];
|
||
error?: string;
|
||
}> {
|
||
return request('GET', '/mcp/servers');
|
||
}
|
||
|
||
/**
|
||
* 获取单个 MCP 服务器详情
|
||
*/
|
||
export async function getMCPServer(name: string): Promise<{
|
||
success: boolean;
|
||
data?: MCPServerStatus;
|
||
error?: string;
|
||
}> {
|
||
return request('GET', `/mcp/servers/${encodeURIComponent(name)}`);
|
||
}
|
||
|
||
/**
|
||
* 连接 MCP 服务器
|
||
*/
|
||
export async function connectMCPServer(name: string): Promise<{
|
||
success: boolean;
|
||
data?: { message: string; status: MCPServerStatus };
|
||
error?: string;
|
||
}> {
|
||
return request('POST', `/mcp/servers/${encodeURIComponent(name)}/connect`);
|
||
}
|
||
|
||
/**
|
||
* 断开 MCP 服务器
|
||
*/
|
||
export async function disconnectMCPServer(name: string): Promise<{
|
||
success: boolean;
|
||
data?: { message: string; status: MCPServerStatus };
|
||
error?: string;
|
||
}> {
|
||
return request('POST', `/mcp/servers/${encodeURIComponent(name)}/disconnect`);
|
||
}
|
||
|
||
/**
|
||
* 启用 MCP 服务器
|
||
*/
|
||
export async function enableMCPServer(name: string): Promise<{
|
||
success: boolean;
|
||
data?: { message: string; status: MCPServerStatus };
|
||
error?: string;
|
||
}> {
|
||
return request('POST', `/mcp/servers/${encodeURIComponent(name)}/enable`);
|
||
}
|
||
|
||
/**
|
||
* 禁用 MCP 服务器
|
||
*/
|
||
export async function disableMCPServer(name: string): Promise<{
|
||
success: boolean;
|
||
data?: { message: string; status: MCPServerStatus };
|
||
error?: string;
|
||
}> {
|
||
return request('POST', `/mcp/servers/${encodeURIComponent(name)}/disable`);
|
||
}
|
||
|
||
/**
|
||
* 获取所有 MCP 工具
|
||
*/
|
||
export async function listMCPTools(): Promise<{
|
||
success: boolean;
|
||
data: MCPToolInfo[];
|
||
error?: string;
|
||
}> {
|
||
return request('GET', '/mcp/tools');
|
||
}
|
||
|
||
/**
|
||
* 获取单个 MCP 工具详情
|
||
*/
|
||
export async function getMCPTool(name: string): Promise<{
|
||
success: boolean;
|
||
data?: MCPToolInfo;
|
||
error?: string;
|
||
}> {
|
||
return request('GET', `/mcp/tools/${encodeURIComponent(name)}`);
|
||
}
|
||
|
||
/**
|
||
* 获取 MCP 配置
|
||
*/
|
||
export async function getMCPConfig(): Promise<{
|
||
success: boolean;
|
||
data?: MCPConfig;
|
||
error?: string;
|
||
}> {
|
||
return request('GET', '/mcp/config');
|
||
}
|
||
|
||
// ============ Hooks API ============
|
||
|
||
/**
|
||
* 获取完整钩子配置
|
||
*/
|
||
export async function getHooksConfig(): Promise<{
|
||
success: boolean;
|
||
data: HookConfig;
|
||
error?: string;
|
||
}> {
|
||
return request('GET', '/hooks/config');
|
||
}
|
||
|
||
/**
|
||
* 更新完整钩子配置
|
||
*/
|
||
export async function updateHooksConfig(config: HookConfig): Promise<{
|
||
success: boolean;
|
||
data: HookConfig;
|
||
error?: string;
|
||
}> {
|
||
return request('PUT', '/hooks/config', config);
|
||
}
|
||
|
||
/**
|
||
* 获取 file_edited 钩子配置
|
||
*/
|
||
export async function getFileEditedHooks(): Promise<{
|
||
success: boolean;
|
||
data: FileHookConfig;
|
||
error?: string;
|
||
}> {
|
||
return request('GET', '/hooks/file-edited');
|
||
}
|
||
|
||
/**
|
||
* 更新 file_edited 钩子配置
|
||
*/
|
||
export async function updateFileEditedHooks(hooks: FileHookConfig): Promise<{
|
||
success: boolean;
|
||
data: FileHookConfig;
|
||
error?: string;
|
||
}> {
|
||
return request('PUT', '/hooks/file-edited', hooks);
|
||
}
|
||
|
||
/**
|
||
* 获取 file_created 钩子配置
|
||
*/
|
||
export async function getFileCreatedHooks(): Promise<{
|
||
success: boolean;
|
||
data: FileHookConfig;
|
||
error?: string;
|
||
}> {
|
||
return request('GET', '/hooks/file-created');
|
||
}
|
||
|
||
/**
|
||
* 更新 file_created 钩子配置
|
||
*/
|
||
export async function updateFileCreatedHooks(hooks: FileHookConfig): Promise<{
|
||
success: boolean;
|
||
data: FileHookConfig;
|
||
error?: string;
|
||
}> {
|
||
return request('PUT', '/hooks/file-created', hooks);
|
||
}
|
||
|
||
/**
|
||
* 获取 file_deleted 钩子配置
|
||
*/
|
||
export async function getFileDeletedHooks(): Promise<{
|
||
success: boolean;
|
||
data: FileHookConfig;
|
||
error?: string;
|
||
}> {
|
||
return request('GET', '/hooks/file-deleted');
|
||
}
|
||
|
||
/**
|
||
* 更新 file_deleted 钩子配置
|
||
*/
|
||
export async function updateFileDeletedHooks(hooks: FileHookConfig): Promise<{
|
||
success: boolean;
|
||
data: FileHookConfig;
|
||
error?: string;
|
||
}> {
|
||
return request('PUT', '/hooks/file-deleted', hooks);
|
||
}
|
||
|
||
/**
|
||
* 获取 session_completed 钩子配置
|
||
*/
|
||
export async function getSessionCompletedHooks(): Promise<{
|
||
success: boolean;
|
||
data: ShellCommandConfig[];
|
||
error?: string;
|
||
}> {
|
||
return request('GET', '/hooks/session-completed');
|
||
}
|
||
|
||
/**
|
||
* 更新 session_completed 钩子配置
|
||
*/
|
||
export async function updateSessionCompletedHooks(hooks: ShellCommandConfig[]): Promise<{
|
||
success: boolean;
|
||
data: ShellCommandConfig[];
|
||
error?: string;
|
||
}> {
|
||
return request('PUT', '/hooks/session-completed', hooks);
|
||
}
|
||
|
||
/**
|
||
* 测试执行钩子命令
|
||
*/
|
||
export async function testHookCommand(command: ShellCommandConfig): Promise<{
|
||
success: boolean;
|
||
data?: HookTestResult;
|
||
error?: string;
|
||
}> {
|
||
return request('POST', '/hooks/test', command);
|
||
}
|
||
|
||
// ============ Agents API ============
|
||
|
||
/**
|
||
* 获取所有 Agent 列表
|
||
*/
|
||
export async function listAgents(): Promise<{
|
||
success: boolean;
|
||
data: AgentListItem[];
|
||
error?: string;
|
||
}> {
|
||
return request('GET', '/agents');
|
||
}
|
||
|
||
/**
|
||
* 获取单个 Agent 详情
|
||
*/
|
||
export async function getAgent(name: string): Promise<{
|
||
success: boolean;
|
||
data?: AgentDetail;
|
||
error?: string;
|
||
}> {
|
||
return request('GET', `/agents/${encodeURIComponent(name)}`);
|
||
}
|
||
|
||
/**
|
||
* 创建新 Agent
|
||
*/
|
||
export async function createAgent(
|
||
name: string,
|
||
config: AgentInput
|
||
): Promise<{
|
||
success: boolean;
|
||
data?: AgentDetail;
|
||
error?: string;
|
||
}> {
|
||
return request('POST', '/agents', { name, ...config });
|
||
}
|
||
|
||
/**
|
||
* 更新 Agent
|
||
*/
|
||
export async function updateAgent(
|
||
name: string,
|
||
config: AgentInput
|
||
): Promise<{
|
||
success: boolean;
|
||
data?: AgentDetail;
|
||
error?: string;
|
||
}> {
|
||
return request('PUT', `/agents/${encodeURIComponent(name)}`, config);
|
||
}
|
||
|
||
/**
|
||
* 删除 Agent
|
||
*/
|
||
export async function deleteAgent(name: string): Promise<{
|
||
success: boolean;
|
||
error?: string;
|
||
}> {
|
||
return request('DELETE', `/agents/${encodeURIComponent(name)}`);
|
||
}
|
||
|
||
/**
|
||
* 获取预设 Agent 列表
|
||
*/
|
||
export async function listPresetAgents(): Promise<{
|
||
success: boolean;
|
||
data: AgentListItem[];
|
||
error?: string;
|
||
}> {
|
||
return request('GET', '/agents/presets');
|
||
}
|
||
|
||
/**
|
||
* 获取全局默认配置
|
||
*/
|
||
export async function getAgentDefaults(): Promise<{
|
||
success: boolean;
|
||
data: AgentDefaults;
|
||
error?: string;
|
||
}> {
|
||
return request('GET', '/agents/defaults');
|
||
}
|
||
|
||
/**
|
||
* 更新全局默认配置
|
||
*/
|
||
export async function updateAgentDefaults(defaults: AgentDefaults): Promise<{
|
||
success: boolean;
|
||
data: AgentDefaults;
|
||
error?: string;
|
||
}> {
|
||
return request('PUT', '/agents/defaults', defaults);
|
||
}
|
||
|
||
// ============ Checkpoints API ============
|
||
|
||
/**
|
||
* 获取所有检查点列表
|
||
*/
|
||
export async function listCheckpoints(): Promise<{
|
||
success: boolean;
|
||
data: CheckpointListItem[];
|
||
error?: string;
|
||
}> {
|
||
return request('GET', '/checkpoints');
|
||
}
|
||
|
||
/**
|
||
* 获取检查点统计信息
|
||
*/
|
||
export async function getCheckpointStats(): Promise<{
|
||
success: boolean;
|
||
data: CheckpointStats;
|
||
error?: string;
|
||
}> {
|
||
return request('GET', '/checkpoints/stats');
|
||
}
|
||
|
||
/**
|
||
* 获取最新检查点
|
||
*/
|
||
export async function getLatestCheckpoint(): Promise<{
|
||
success: boolean;
|
||
data: CheckpointDetail | null;
|
||
error?: string;
|
||
}> {
|
||
return request('GET', '/checkpoints/latest');
|
||
}
|
||
|
||
/**
|
||
* 获取单个检查点详情
|
||
*/
|
||
export async function getCheckpoint(id: string): Promise<{
|
||
success: boolean;
|
||
data?: CheckpointDetail;
|
||
error?: string;
|
||
}> {
|
||
return request('GET', `/checkpoints/${encodeURIComponent(id)}`);
|
||
}
|
||
|
||
/**
|
||
* 创建手动检查点
|
||
*/
|
||
export async function createCheckpoint(options?: {
|
||
name?: string;
|
||
description?: string;
|
||
}): Promise<{
|
||
success: boolean;
|
||
data?: CheckpointDetail;
|
||
error?: string;
|
||
}> {
|
||
return request('POST', '/checkpoints', options || {});
|
||
}
|
||
|
||
/**
|
||
* 删除检查点
|
||
*/
|
||
export async function deleteCheckpoint(id: string): Promise<{
|
||
success: boolean;
|
||
data?: { deleted: boolean };
|
||
error?: string;
|
||
}> {
|
||
return request('DELETE', `/checkpoints/${encodeURIComponent(id)}`);
|
||
}
|
||
|
||
/**
|
||
* 获取检查点与当前工作区的差异
|
||
*/
|
||
export async function getCheckpointDiff(id: string): Promise<{
|
||
success: boolean;
|
||
data?: DiffInfo;
|
||
error?: string;
|
||
}> {
|
||
return request('GET', `/checkpoints/${encodeURIComponent(id)}/diff`);
|
||
}
|
||
|
||
/**
|
||
* 获取单个文件的详细差异
|
||
*/
|
||
export async function getFileDiff(checkpointId: string, filePath: string): Promise<{
|
||
success: boolean;
|
||
data?: FileDiffDetail;
|
||
error?: string;
|
||
}> {
|
||
const params = new URLSearchParams({ path: filePath });
|
||
return request('GET', `/checkpoints/${encodeURIComponent(checkpointId)}/file-diff?${params}`);
|
||
}
|
||
|
||
/**
|
||
* 回滚到检查点
|
||
*/
|
||
export async function restoreCheckpoint(
|
||
id: string,
|
||
options?: RestoreOptions
|
||
): Promise<{
|
||
success: boolean;
|
||
data?: RestoreResult;
|
||
error?: string;
|
||
}> {
|
||
return request('POST', `/checkpoints/${encodeURIComponent(id)}/restore`, options || {});
|
||
}
|
||
|
||
/**
|
||
* 预览回滚(dry run)
|
||
*/
|
||
export async function previewRestore(
|
||
id: string,
|
||
options?: { mode?: RestoreOptions['mode']; files?: string[] }
|
||
): Promise<{
|
||
success: boolean;
|
||
data?: RestoreResult;
|
||
error?: string;
|
||
}> {
|
||
const params = new URLSearchParams();
|
||
if (options?.mode) params.set('mode', options.mode);
|
||
if (options?.files) params.set('files', options.files.join(','));
|
||
return request('GET', `/checkpoints/${encodeURIComponent(id)}/restore/preview?${params}`);
|
||
}
|
||
|
||
/**
|
||
* 撤销最近一次回滚
|
||
*/
|
||
export async function unrevert(): Promise<{
|
||
success: boolean;
|
||
data?: UnrevertResult;
|
||
error?: string;
|
||
}> {
|
||
return request('POST', '/checkpoints/unrevert');
|
||
}
|
||
|
||
/**
|
||
* 检查是否可撤销回滚
|
||
*/
|
||
export async function getUnrevertStatus(): Promise<{
|
||
success: boolean;
|
||
data?: UnrevertStatus;
|
||
error?: string;
|
||
}> {
|
||
return request('GET', '/checkpoints/unrevert/status');
|
||
}
|
||
|
||
/**
|
||
* 执行安全检查
|
||
*/
|
||
export async function checkSafety(id: string): Promise<{
|
||
success: boolean;
|
||
data?: SafetyCheckResult;
|
||
error?: string;
|
||
}> {
|
||
return request('GET', `/checkpoints/${encodeURIComponent(id)}/safety-check`);
|
||
}
|
||
|
||
/**
|
||
* 清理过期检查点
|
||
*/
|
||
export async function cleanupCheckpoints(): Promise<{
|
||
success: boolean;
|
||
data?: { deleted: number };
|
||
error?: string;
|
||
}> {
|
||
return request('POST', '/checkpoints/cleanup');
|
||
}
|
||
|
||
/**
|
||
* 获取会话的所有检查点
|
||
*/
|
||
export async function getSessionCheckpoints(sessionId: string): Promise<{
|
||
success: boolean;
|
||
data: CheckpointListItem[];
|
||
error?: string;
|
||
}> {
|
||
return request('GET', `/checkpoints/sessions/${encodeURIComponent(sessionId)}`);
|
||
}
|
||
|
||
/**
|
||
* 获取消息关联的检查点
|
||
*/
|
||
export async function getMessageCheckpoints(messageId: string): Promise<{
|
||
success: boolean;
|
||
data: CheckpointListItem[];
|
||
error?: string;
|
||
}> {
|
||
return request('GET', `/checkpoints/messages/${encodeURIComponent(messageId)}`);
|
||
}
|
||
|
||
// ============ Providers API ============
|
||
|
||
/**
|
||
* 获取所有提供商列表
|
||
*/
|
||
export async function listProviders(): Promise<{
|
||
success: boolean;
|
||
data: ProviderListItem[];
|
||
error?: string;
|
||
}> {
|
||
return request('GET', '/providers');
|
||
}
|
||
|
||
/**
|
||
* 获取单个提供商详情
|
||
*/
|
||
export async function getProvider(id: string): Promise<{
|
||
success: boolean;
|
||
data?: ProviderDetail;
|
||
error?: string;
|
||
}> {
|
||
return request('GET', `/providers/${encodeURIComponent(id)}`);
|
||
}
|
||
|
||
/**
|
||
* 获取提供商的模型列表
|
||
*/
|
||
export async function getProviderModels(id: string): Promise<{
|
||
success: boolean;
|
||
data: ModelInfo[];
|
||
error?: string;
|
||
}> {
|
||
return request('GET', `/providers/${encodeURIComponent(id)}/models`);
|
||
}
|
||
|
||
/**
|
||
* 测试提供商连接
|
||
*/
|
||
export async function testProviderConnection(
|
||
id: string,
|
||
apiKey?: string
|
||
): Promise<{
|
||
success: boolean;
|
||
data: ConnectionTestResult;
|
||
error?: string;
|
||
}> {
|
||
return request('POST', `/providers/${encodeURIComponent(id)}/test`, apiKey ? { apiKey } : {});
|
||
}
|
||
|
||
/**
|
||
* 注册自定义提供商
|
||
*/
|
||
export async function registerProvider(
|
||
definition: CustomProviderDefinition
|
||
): Promise<{
|
||
success: boolean;
|
||
message?: string;
|
||
error?: string;
|
||
}> {
|
||
return request('POST', '/providers', definition);
|
||
}
|
||
|
||
/**
|
||
* 更新提供商配置
|
||
*/
|
||
export async function updateProviderConfig(
|
||
id: string,
|
||
config: ProviderConfig
|
||
): Promise<{
|
||
success: boolean;
|
||
message?: string;
|
||
error?: string;
|
||
}> {
|
||
return request('PUT', `/providers/${encodeURIComponent(id)}`, config);
|
||
}
|
||
|
||
/**
|
||
* 删除自定义提供商
|
||
*/
|
||
export async function deleteProvider(id: string): Promise<{
|
||
success: boolean;
|
||
message?: string;
|
||
error?: string;
|
||
}> {
|
||
return request('DELETE', `/providers/${encodeURIComponent(id)}`);
|
||
}
|
||
|
||
/**
|
||
* 添加自定义模型
|
||
*/
|
||
export async function addProviderModel(
|
||
providerId: string,
|
||
model: ModelInfo
|
||
): Promise<{
|
||
success: boolean;
|
||
message?: string;
|
||
error?: string;
|
||
}> {
|
||
return request('POST', `/providers/${encodeURIComponent(providerId)}/models`, model);
|
||
}
|
||
|
||
/**
|
||
* 删除自定义模型
|
||
*/
|
||
export async function deleteProviderModel(
|
||
providerId: string,
|
||
modelId: string
|
||
): Promise<{
|
||
success: boolean;
|
||
message?: string;
|
||
error?: string;
|
||
}> {
|
||
return request('DELETE', `/providers/${encodeURIComponent(providerId)}/models/${encodeURIComponent(modelId)}`);
|
||
}
|
||
|
||
// ============ Context Compression API ============
|
||
|
||
/**
|
||
* 获取会话上下文使用情况
|
||
*/
|
||
export async function getContextUsage(sessionId: string): Promise<{
|
||
success: boolean;
|
||
data?: ContextUsageInfo;
|
||
error?: string;
|
||
}> {
|
||
return request('GET', `/sessions/${encodeURIComponent(sessionId)}/context`);
|
||
}
|
||
|
||
/**
|
||
* 触发上下文压缩
|
||
*/
|
||
export async function compressContext(
|
||
sessionId: string,
|
||
options?: { force?: boolean }
|
||
): Promise<{
|
||
success: boolean;
|
||
data?: CompressionResult;
|
||
error?: string;
|
||
}> {
|
||
return request('POST', `/sessions/${encodeURIComponent(sessionId)}/compress`, options || {});
|
||
}
|
||
|
||
// ============ File Search API ============
|
||
|
||
/**
|
||
* 模糊搜索项目文件
|
||
*/
|
||
export async function searchFiles(
|
||
query: string = '',
|
||
limit: number = 10,
|
||
type: 'file' | 'directory' | 'all' = 'file'
|
||
): Promise<FileSearchResponse> {
|
||
const params = new URLSearchParams({
|
||
query,
|
||
limit: String(limit),
|
||
type,
|
||
});
|
||
return request('GET', `/files/search?${params}`);
|
||
}
|
||
|
||
// ============ LSP API ============
|
||
|
||
/**
|
||
* 获取所有语言服务器列表
|
||
*/
|
||
export async function listLSPServers(): Promise<{
|
||
success: boolean;
|
||
data: LSPServer[];
|
||
error?: string;
|
||
}> {
|
||
return request('GET', '/lsp/servers');
|
||
}
|
||
|
||
/**
|
||
* 获取单个语言服务器详情
|
||
*/
|
||
export async function getLSPServer(id: string): Promise<{
|
||
success: boolean;
|
||
data?: LSPServer;
|
||
error?: string;
|
||
}> {
|
||
return request('GET', `/lsp/servers/${encodeURIComponent(id)}`);
|
||
}
|
||
|
||
/**
|
||
* 安装语言服务器
|
||
*/
|
||
export async function installLSPServer(id: string): Promise<{
|
||
success: boolean;
|
||
data?: { message: string; server: LSPServer };
|
||
error?: string;
|
||
}> {
|
||
return request('POST', `/lsp/servers/${encodeURIComponent(id)}/install`);
|
||
}
|
||
|
||
/**
|
||
* 启动语言服务器
|
||
* @param id 服务器 ID
|
||
* @param filePath 需要提供一个文件路径来触发对应语言的服务器
|
||
*/
|
||
export async function startLSPServer(
|
||
id: string,
|
||
filePath: string
|
||
): Promise<{
|
||
success: boolean;
|
||
data?: { message: string; isFirstStart: boolean; runningServers: string[] };
|
||
error?: string;
|
||
}> {
|
||
return request('POST', `/lsp/servers/${encodeURIComponent(id)}/start`, { filePath });
|
||
}
|
||
|
||
/**
|
||
* 停止语言服务器
|
||
*/
|
||
export async function stopLSPServer(id: string): Promise<{
|
||
success: boolean;
|
||
data?: { message: string; runningServers: string[] };
|
||
error?: string;
|
||
}> {
|
||
return request('POST', `/lsp/servers/${encodeURIComponent(id)}/stop`);
|
||
}
|
||
|
||
/**
|
||
* 获取正在运行的语言服务器列表
|
||
*/
|
||
export async function getRunningLSPServers(): Promise<{
|
||
success: boolean;
|
||
data: string[];
|
||
error?: string;
|
||
}> {
|
||
return request('GET', '/lsp/running');
|
||
}
|
||
|
||
/**
|
||
* 获取诊断信息
|
||
* @param file 可选,指定文件路径获取单个文件的诊断
|
||
*/
|
||
export async function getLSPDiagnostics(file?: string): Promise<{
|
||
success: boolean;
|
||
data: DiagnosticsResponse;
|
||
error?: string;
|
||
}> {
|
||
const params = file ? `?file=${encodeURIComponent(file)}` : '';
|
||
return request('GET', `/lsp/diagnostics${params}`);
|
||
}
|