Files
metona-ollama-desktop/src/main/preload.ts
T
thzxx 5bfb137a8a feat: v3.0 Tool Calling — AI 本地文件操作系统
新增文件:
- src/main/tool-security.ts: 路径白名单/黑名单、命令安全检查
- src/main/tool-handlers.ts: 7个工具实现(read/write/list/search/create/delete/run)
- src/renderer/services/tool-registry.ts: 工具注册调度中心
- src/renderer/services/agent-engine.ts: Agent Loop 流式多轮工具调用引擎
- src/renderer/components/tool-confirm-modal.ts: 高风险操作确认对话框

修改文件:
- types.d.ts: 新增 ToolCall/ToolResult/ToolCallRecord 等类型
- ollama.ts: chatStream 支持 tools 参数
- ipc.ts: 新增 tool:execute/getConfig/setAllowedDirs IPC
- preload.ts: 暴露 tool API
- chat-area.ts: 渲染工具调用卡片
- input-area.ts: 集成 Agent Loop 引擎
- settings-modal.ts: 工具调用开关设置
- index.html: 工具调用设置面板 + 确认对话框 HTML
- style.css: 工具调用卡片、确认对话框样式
- main.ts: 初始化工具调用配置
2026-04-06 13:29:43 +08:00

40 lines
1.6 KiB
TypeScript

/**
* Metona Ollama Desktop - Preload 脚本
*/
import { contextBridge, ipcRenderer } from 'electron';
contextBridge.exposeInMainWorld('metonaDesktop', {
isDesktop: true,
info: () => ipcRenderer.invoke('app:info'),
dialog: {
openFile: (filters?: unknown) => ipcRenderer.invoke('dialog:openFile', { filters }),
saveFile: (options?: unknown) => ipcRenderer.invoke('dialog:saveFile', options)
},
fs: {
readFile: (filePath: string) => ipcRenderer.invoke('fs:readFile', filePath),
writeFile: (filePath: string, content: string) => ipcRenderer.invoke('fs:writeFile', filePath, content)
},
tool: {
execute: (toolName: string, args: Record<string, unknown>) => ipcRenderer.invoke('tool:execute', toolName, args),
getConfig: () => ipcRenderer.invoke('tool:getConfig'),
setAllowedDirs: (dirs: string[]) => ipcRenderer.invoke('tool:setAllowedDirs', dirs)
},
notify: (title: string, body: string) => ipcRenderer.invoke('notify', title, body),
window: {
minimize: () => ipcRenderer.invoke('window:minimize'),
maximize: () => ipcRenderer.invoke('window:maximize'),
close: () => ipcRenderer.invoke('window:close')
},
openExternal: (url: string) => ipcRenderer.invoke('shell:openExternal', url),
onMenuAction: (callback: (action: string) => void) => {
ipcRenderer.on('menu-action', (_: unknown, action: string) => callback(action));
},
onTrayAction: (callback: (action: string) => void) => {
ipcRenderer.on('tray-action', (_: unknown, action: string) => callback(action));
},
removeAllListeners: (channel: string) => {
ipcRenderer.removeAllListeners(channel);
}
});