Files
metona-ollama-desktop/src/main/preload.ts
T
thzxx 0431ed56c0 refactor: 命令执行走工作空间终端,AI 不再直接执行
流程改为:AI 提出命令 → 用户确认 → 工作空间终端执行(实时显示)→ 结果反馈 AI → AI 回复

- handleRunCommand 直接 spawn 子进程,stdout/stderr 实时通过 cmd:output 推送
- 进程结束后通过 cmd:done 通知渲染进程
- 停止按钮通过 cmd:kill 终止进程
- 清理 workspace.ts 中不再需要的 execWorkspaceCommand 工具集成代码
- 去掉 workspace:execTool/killTool,改为 cmd:output/cmd:done/cmd:kill
2026-04-08 13:12:18 +08:00

76 lines
3.7 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, encoding?: string) => ipcRenderer.invoke('fs:writeFile', filePath, content, encoding)
},
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));
},
onAppQuit: (callback: () => void) => {
ipcRenderer.on('app-quit', () => callback());
},
removeAllListeners: (channel: string) => {
ipcRenderer.removeAllListeners(channel);
},
onMainLog: (callback: (data: { level: string; message: string; detail?: string }) => void) => {
ipcRenderer.on('main:log', (_: unknown, data: { level: string; message: string; detail?: string }) => callback(data));
},
workspace: {
getDir: () => ipcRenderer.invoke('workspace:getDir'),
setDir: (dir: string) => ipcRenderer.invoke('workspace:setDir', dir),
listDir: (dirPath?: string) => ipcRenderer.invoke('workspace:listDir', dirPath),
readFile: (filePath: string) => ipcRenderer.invoke('workspace:readFile', filePath),
exec: (params: { id: string; command: string; cwd?: string }) => {
ipcRenderer.send('workspace:exec', params);
},
kill: (id: string) => {
ipcRenderer.send('workspace:kill', id);
},
onOutput: (callback: (data: { id: string; type: 'stdout' | 'stderr'; data: string }) => void) => {
ipcRenderer.on('workspace:output', (_: unknown, data: { id: string; type: 'stdout' | 'stderr'; data: string }) => callback(data));
},
onExit: (callback: (data: { id: string; code: number | null }) => void) => {
ipcRenderer.on('workspace:exit', (_: unknown, data: { id: string; code: number | null }) => callback(data));
},
/** 无超时工具命令执行,用于 AI Tool Calling */
execTool: (command: string, cwd?: string) => ipcRenderer.invoke('tool:execute', 'run_command', { command, cwd }),
/** AI 命令实时输出推送到工作空间终端 */
onCmdOutput: (callback: (data: { command: string; type: 'stdout' | 'stderr'; data: string }) => void) => {
ipcRenderer.on('cmd:output', (_: unknown, data: { command: string; type: 'stdout' | 'stderr'; data: string }) => callback(data));
},
/** AI 命令执行完毕 */
onCmdDone: (callback: (data: { command: string; exitCode: number | null }) => void) => {
ipcRenderer.on('cmd:done', (_: unknown, data: { command: string; exitCode: number | null }) => callback(data));
},
/** 终止当前 AI 命令 */
cmdKill: () => ipcRenderer.invoke('cmd:kill')
}
});