- Electron + React + TypeScript 架构 - 三栏布局: Sidebar | ChatPanel | DetailPanel - 9 个内置工具 (文件系统/网络/记忆/命令) - SQLite 持久化 (better-sqlite3) - MUI 暗色/亮色主题系统 - Agent Loop ReAct 状态机引擎 - DeepSeek / Agnes AI / Ollama Provider 适配器 - MCP 协议集成 - 系统托盘 + 全局快捷键 - Tailwind CSS v4 + Tailwind Merge - 修复: Sidebar 缺失 TextField 导入导致黑屏
178 lines
4.6 KiB
TypeScript
178 lines
4.6 KiB
TypeScript
/**
|
|
* IPC Client — 渲染进程 IPC 封装
|
|
*
|
|
* 为渲染进程提供类型安全的 IPC 调用封装。
|
|
* 所有调用通过 window.metona 桥接层(由 preload.ts contextBridge 暴露)。
|
|
*
|
|
* @see electron/preload.ts — contextBridge 安全暴露
|
|
* @see src/types/global.d.ts — 类型声明
|
|
*/
|
|
|
|
import type { MetonaBridge, MetonaSessionInfo, MetonaStreamEventData, MetonaMCPServerStatus, MetonaMemorySearchResult } from '@renderer/types/global';
|
|
|
|
// ===== 桥接层访问 =====
|
|
|
|
function getBridge(): MetonaBridge {
|
|
if (!window.metona) {
|
|
throw new Error('Metona bridge not available. Ensure preload script is loaded.');
|
|
}
|
|
return window.metona;
|
|
}
|
|
|
|
// ===== Agent API =====
|
|
|
|
export const agentAPI = {
|
|
/**
|
|
* 发送用户消息到 Agent
|
|
*/
|
|
sendMessage(message: { role: 'user'; content: string; timestamp: number }, sessionId: string): Promise<{ success: boolean }> {
|
|
return getBridge().agent.sendMessage(message, sessionId);
|
|
},
|
|
|
|
/**
|
|
* 监听流式事件
|
|
* @returns 取消监听函数
|
|
*/
|
|
onStreamEvent(callback: (event: MetonaStreamEventData) => void): () => void {
|
|
return getBridge().agent.onStreamEvent(callback);
|
|
},
|
|
|
|
/**
|
|
* 监听状态变化
|
|
* @returns 取消监听函数
|
|
*/
|
|
onStateChange(callback: (state: { sessionId: string; state: string }) => void): () => void {
|
|
return getBridge().agent.onStateChange(callback);
|
|
},
|
|
|
|
/**
|
|
* 中断当前会话
|
|
*/
|
|
abortSession(sessionId: string): Promise<{ success: boolean }> {
|
|
return getBridge().agent.abortSession(sessionId);
|
|
},
|
|
|
|
/**
|
|
* 监听 Provider 切换通知
|
|
* @returns 取消监听函数
|
|
*/
|
|
onProviderSwitched(callback: (data: { from?: string; to?: string; reason?: string }) => void): () => void {
|
|
return getBridge().agent.onProviderSwitched(callback);
|
|
},
|
|
};
|
|
|
|
// ===== Sessions API =====
|
|
|
|
export const sessionsAPI = {
|
|
list(): Promise<MetonaSessionInfo[]> {
|
|
return getBridge().sessions.list();
|
|
},
|
|
|
|
create(title?: string): Promise<MetonaSessionInfo> {
|
|
return getBridge().sessions.create(title);
|
|
},
|
|
|
|
rename(sessionId: string, title: string): Promise<{ success: boolean }> {
|
|
return getBridge().sessions.rename(sessionId, title);
|
|
},
|
|
|
|
delete(sessionId: string): Promise<{ success: boolean }> {
|
|
return getBridge().sessions.delete(sessionId);
|
|
},
|
|
|
|
getMessages(sessionId: string): Promise<Array<{
|
|
id: string;
|
|
role: string;
|
|
content: string;
|
|
reasoningContent?: string;
|
|
toolCalls?: unknown[];
|
|
timestamp: number;
|
|
}>> {
|
|
return getBridge().sessions.getMessages(sessionId);
|
|
},
|
|
|
|
pin(sessionId: string, pinned: boolean): Promise<{ success: boolean }> {
|
|
return getBridge().sessions.pin(sessionId, pinned);
|
|
},
|
|
};
|
|
|
|
// ===== MCP API =====
|
|
|
|
export const mcpAPI = {
|
|
listServers(): Promise<MetonaMCPServerStatus[]> {
|
|
return getBridge().mcp.listServers();
|
|
},
|
|
|
|
addServer(config: { name: string; transport: 'stdio' | 'sse'; command?: string; args?: string[]; url?: string }): Promise<{ success: boolean }> {
|
|
return getBridge().mcp.addServer(config);
|
|
},
|
|
|
|
removeServer(name: string): Promise<{ success: boolean }> {
|
|
return getBridge().mcp.removeServer(name);
|
|
},
|
|
|
|
toggleServer(name: string, enabled: boolean): Promise<{ success: boolean }> {
|
|
return getBridge().mcp.toggleServer(name, enabled);
|
|
},
|
|
};
|
|
|
|
// ===== Memory API =====
|
|
|
|
export const memoryAPI = {
|
|
search(query: string, options?: {
|
|
topK?: number;
|
|
type?: 'episodic' | 'semantic' | 'working';
|
|
minImportance?: number;
|
|
}): Promise<MetonaMemorySearchResult[]> {
|
|
return getBridge().memory.search(query, options);
|
|
},
|
|
};
|
|
|
|
// ===== Config API =====
|
|
|
|
export const configAPI = {
|
|
get<T = unknown>(key: string): Promise<T | null> {
|
|
return getBridge().config.get(key) as Promise<T | null>;
|
|
},
|
|
|
|
set(key: string, value: unknown): Promise<{ success: boolean }> {
|
|
return getBridge().config.set(key, value);
|
|
},
|
|
};
|
|
|
|
// ===== App API =====
|
|
|
|
export const appAPI = {
|
|
getVersion(): Promise<string> {
|
|
return getBridge().app.getVersion();
|
|
},
|
|
|
|
getAppDataPath(): Promise<string> {
|
|
return getBridge().app.getAppDataPath();
|
|
},
|
|
|
|
openExternal(url: string): Promise<void> {
|
|
return getBridge().app.openExternal(url);
|
|
},
|
|
|
|
showItemInFolder(path: string): void {
|
|
getBridge().app.showItemInFolder(path);
|
|
},
|
|
};
|
|
|
|
// ===== Toast API =====
|
|
|
|
export const toastAPI = {
|
|
/**
|
|
* 监听主进程 Toast 通知桥接
|
|
* @returns 取消监听函数
|
|
*/
|
|
onShow(callback: (data: {
|
|
type: 'success' | 'error' | 'warning' | 'info';
|
|
message: string;
|
|
options?: Record<string, unknown>;
|
|
}) => void): () => void {
|
|
return getBridge().toast.onShow(callback);
|
|
},
|
|
};
|