/** * MetonaAI Desktop — Preload 脚本 * * 通过 contextBridge 安全地将 IPC 通道暴露给渲染进程。 * 渲染进程无 Node.js 访问权限,只能通过此桥接层通信。 */ import { contextBridge, ipcRenderer } from 'electron'; import { electronAPI } from '@electron-toolkit/preload'; /** * Metona API — 暴露给渲染进程的安全接口 */ const metonaAPI = { // ===== Agent 交互 ===== agent: { /** 发送用户消息 */ sendMessage: (message: unknown, sessionId: string) => ipcRenderer.invoke('agent:sendMessage', message, sessionId), /** 监听流式事件 */ onStreamEvent: (callback: (event: unknown) => void) => { const listener = (_event: Electron.IpcRendererEvent, data: unknown) => callback(data); ipcRenderer.on('agent:streamEvent', listener); return () => ipcRenderer.removeListener('agent:streamEvent', listener); }, /** 监听状态变化 */ onStateChange: (callback: (state: unknown) => void) => { const listener = (_event: Electron.IpcRendererEvent, data: unknown) => callback(data); ipcRenderer.on('agent:stateChange', listener); return () => ipcRenderer.removeListener('agent:stateChange', listener); }, /** 中断会话 */ abortSession: (sessionId: string) => ipcRenderer.invoke('agent:abortSession', sessionId), /** 监听 Provider 切换通知 */ onProviderSwitched: (callback: (data: unknown) => void) => { const listener = (_event: Electron.IpcRendererEvent, data: unknown) => callback(data); ipcRenderer.on('agent:providerSwitched', listener); return () => ipcRenderer.removeListener('agent:providerSwitched', listener); }, }, // ===== 会话管理 ===== sessions: { list: () => ipcRenderer.invoke('sessions:list'), create: (title?: string) => ipcRenderer.invoke('sessions:create', title), rename: (sessionId: string, title: string) => ipcRenderer.invoke('sessions:rename', sessionId, title), delete: (sessionId: string) => ipcRenderer.invoke('sessions:delete', sessionId), getMessages: (sessionId: string) => ipcRenderer.invoke('sessions:getMessages', sessionId), pin: (sessionId: string, pinned: boolean) => ipcRenderer.invoke('sessions:pin', sessionId, pinned), archive: (sessionId: string, archived: boolean) => ipcRenderer.invoke('sessions:archive', sessionId, archived), deleteMessage: (messageId: string) => ipcRenderer.invoke('sessions:deleteMessage', messageId), clearMessages: (sessionId: string) => ipcRenderer.invoke('sessions:clearMessages', sessionId), saveTrace: (sessionId: string, data: unknown) => ipcRenderer.invoke('sessions:saveTrace', sessionId, data), getTrace: (sessionId: string) => ipcRenderer.invoke('sessions:getTrace', sessionId), }, // ===== MCP 管理 ===== mcp: { listServers: () => ipcRenderer.invoke('mcp:listServers'), addServer: (config: unknown) => ipcRenderer.invoke('mcp:addServer', config), removeServer: (name: string) => ipcRenderer.invoke('mcp:removeServer', name), toggleServer: (name: string, enabled: boolean) => ipcRenderer.invoke('mcp:toggleServer', name, enabled), }, // ===== 记忆系统 ===== memory: { search: (query: string, options?: unknown) => ipcRenderer.invoke('db:searchMemories', query, options), listAll: (options?: { type?: string; limit?: number }) => ipcRenderer.invoke('memory:listAll', options), delete: (type: string, id: string) => ipcRenderer.invoke('memory:delete', type, id), }, // ===== v0.2.0: 任务管理 ===== tasks: { list: (sessionId?: string) => ipcRenderer.invoke('tasks:list', sessionId), create: (data: { sessionId: string; title: string; description?: string; priority?: string; parentId?: string }) => ipcRenderer.invoke('tasks:create', data), update: (id: string, updates: { title?: string; description?: string; status?: string; priority?: string; assignedTo?: string }) => ipcRenderer.invoke('tasks:update', id, updates), delete: (id: string) => ipcRenderer.invoke('tasks:delete', id), }, // ===== v0.2.0: 审计日志 ===== audit: { verifyChain: () => ipcRenderer.invoke('audit:verifyChain'), query: (filters?: { sessionId?: string; eventType?: string; limit?: number }) => ipcRenderer.invoke('audit:query', filters), }, // ===== v0.2.0: 工具确认请求 ===== tool: { onConfirmationRequest: (callback: (request: unknown) => void) => { const listener = (_event: Electron.IpcRendererEvent, data: unknown) => callback(data); ipcRenderer.on('tool:confirmationRequest', listener); return () => ipcRenderer.removeListener('tool:confirmationRequest', listener); }, sendConfirmationResponse: (response: { toolCallId: string; approved: boolean; remember: boolean; autoExecute?: boolean }) => ipcRenderer.send('tool:confirmationResponse', response), // v0.2.0: 持久化自动执行设置 setAutoExecute: (toolName: string, enabled: boolean) => ipcRenderer.invoke('tool:setAutoExecute', toolName, enabled), getAutoExecuteList: () => ipcRenderer.invoke('tool:getAutoExecuteList'), }, // ===== 配置 ===== config: { get: (key: string) => ipcRenderer.invoke('config:get', key), set: (key: string, value: unknown) => ipcRenderer.invoke('config:set', key, value), }, // ===== 应用工具 ===== app: { getVersion: () => ipcRenderer.invoke('app:getVersion'), getAppDataPath: () => ipcRenderer.invoke('app:getAppDataPath'), openExternal: (url: string) => ipcRenderer.invoke('app:openExternal', url), showItemInFolder: (path: string) => ipcRenderer.invoke('app:showItemInFolder', path), selectFolder: (defaultPath?: string) => ipcRenderer.invoke('app:selectFolder', defaultPath), restart: () => ipcRenderer.invoke('app:restart'), }, // ===== 工作空间管理 ===== workspace: { check: (targetPath: string) => ipcRenderer.invoke('workspace:check', targetPath), inheritFiles: (params: { targetPath: string; sourcePath: string; files: string[] }) => ipcRenderer.invoke('workspace:inheritFiles', params), }, // ===== 工具管理 ===== tools: { list: () => ipcRenderer.invoke('tools:list'), toggle: (toolName: string, enabled: boolean) => ipcRenderer.invoke('tools:toggle', toolName, enabled), }, // ===== 数据管理 ===== data: { exportData: (sessionId?: string) => ipcRenderer.invoke('data:export', sessionId), clearSessions: () => ipcRenderer.invoke('data:clearSessions'), clearMemories: () => ipcRenderer.invoke('data:clearMemories'), clearAuditLogs: () => ipcRenderer.invoke('data:clearAuditLogs'), }, // ===== SearXNG ===== searxng: { testConnection: (url: string, authKey: string, authType: string) => ipcRenderer.invoke('searxng:testConnection', url, authKey, authType), }, // ===== Toast 通知桥接 ===== toast: { onShow: (callback: (data: { type: string; message: string; options?: unknown }) => void) => { const listener = (_event: Electron.IpcRendererEvent, data: { type: string; message: string; options?: unknown }) => callback(data); ipcRenderer.on('toast:show', listener); return () => ipcRenderer.removeListener('toast:show', listener); }, }, }; // ===== 安全暴露到渲染进程 ===== if (process.contextIsolated) { try { contextBridge.exposeInMainWorld('electron', electronAPI); contextBridge.exposeInMainWorld('metona', metonaAPI); } catch (error) { console.error('Failed to expose APIs:', error); } } else { // 降级方案(不推荐) (globalThis as unknown as Record).electron = electronAPI; (globalThis as unknown as Record).metona = metonaAPI; }