refactor: 工作空间去掉手动命令输入,终止时自动反馈 AI
- 移除工作空间终端的命令输入框和执行按钮 - 用户只能通过终止按钮中断卡住的 AI 命令 - 终止后自动通过 killTool IPC 终止进程,结果自动回传 AI - 新增 workspace:killTool IPC 和 terminateCurrentToolCommand - 新增终端底部状态提示(等待/执行中) - 修复工作空间面板顶部对齐(90px → 92px)
This commit is contained in:
+8
-1
@@ -22,7 +22,7 @@ import {
|
||||
handleRunCommand
|
||||
} from './tool-handlers.js';
|
||||
import { getAllowedDirs, getBlockedDirs, setAllowedDirs } from './tool-security.js';
|
||||
import { startProcess, killProcess, getWorkspaceDir, setWorkspaceDir, listWorkspaceDir, execWorkspaceCommand } from './workspace.js';
|
||||
import { startProcess, killProcess, getWorkspaceDir, setWorkspaceDir, listWorkspaceDir, execWorkspaceCommand, terminateCurrentToolCommand } from './workspace.js';
|
||||
|
||||
export function setupIPC(): void {
|
||||
ipcMain.handle('dialog:openFile', async (_, options?: { filters?: Array<{ name: string; extensions: string[] }> }) => {
|
||||
@@ -177,4 +177,11 @@ export function setupIPC(): void {
|
||||
sendLog('info', `🔧 workspace:execTool`, `${command.slice(0, 200)} | cwd: ${cwd || '默认'}`);
|
||||
return execWorkspaceCommand(command, cwd);
|
||||
});
|
||||
|
||||
// 终止当前 AI 工具命令
|
||||
ipcMain.handle('workspace:killTool', async () => {
|
||||
const killed = terminateCurrentToolCommand();
|
||||
sendLog('info', `🔧 workspace:killTool`, killed ? '已终止' : '无正在运行的工具命令');
|
||||
return { killed };
|
||||
});
|
||||
}
|
||||
|
||||
@@ -61,6 +61,8 @@ contextBridge.exposeInMainWorld('metonaDesktop', {
|
||||
},
|
||||
/** 无超时命令执行,用于 AI Tool Calling */
|
||||
execTool: (command: string, cwd?: string) => ipcRenderer.invoke('workspace:execTool', command, cwd),
|
||||
/** 终止当前 AI 工具命令 */
|
||||
killTool: () => ipcRenderer.invoke('workspace:killTool'),
|
||||
/** AI Tool Calling 命令输出 → 同步到工作空间终端 */
|
||||
onToolOutput: (callback: (data: { command: string; stdout: string; stderr: string; exitCode: number | null }) => void) => {
|
||||
ipcRenderer.on('workspace:toolOutput', (_: unknown, data: { command: string; stdout: string; stderr: string; exitCode: number | null }) => callback(data));
|
||||
|
||||
@@ -20,6 +20,9 @@ export const DEFAULT_WORKSPACE_DIR = path.join(app.getPath('userData'), 'workspa
|
||||
/** 活跃进程 Map */
|
||||
const activeProcesses = new Map<string, ChildProcess>();
|
||||
|
||||
/** 当前 AI 工具命令进程 ID(用于用户手动终止) */
|
||||
let _currentToolProcessId: string | null = null;
|
||||
|
||||
/** 确保工作空间目录存在 */
|
||||
export function ensureWorkspaceDir(): void {
|
||||
if (!fs.existsSync(DEFAULT_WORKSPACE_DIR)) {
|
||||
@@ -253,6 +256,7 @@ export function execWorkspaceCommand(
|
||||
});
|
||||
|
||||
activeProcesses.set(cmdId, proc);
|
||||
_currentToolProcessId = cmdId;
|
||||
|
||||
proc.stdout.on('data', (data: Buffer) => {
|
||||
if (stdoutBuf.length < MAX_OUTPUT) {
|
||||
@@ -280,6 +284,7 @@ export function execWorkspaceCommand(
|
||||
|
||||
proc.on('close', (code) => {
|
||||
activeProcesses.delete(cmdId);
|
||||
if (_currentToolProcessId === cmdId) _currentToolProcessId = null;
|
||||
const duration = Date.now() - startTime;
|
||||
sendLog(
|
||||
code === 0 ? 'success' : 'warn',
|
||||
@@ -299,6 +304,7 @@ export function execWorkspaceCommand(
|
||||
|
||||
proc.on('error', (err) => {
|
||||
activeProcesses.delete(cmdId);
|
||||
if (_currentToolProcessId === cmdId) _currentToolProcessId = null;
|
||||
const duration = Date.now() - startTime;
|
||||
sendLog('error', `🔧 execWorkspaceCommand 失败`, `${cmdId} | ${err.message}`);
|
||||
resolve({
|
||||
@@ -334,3 +340,14 @@ export function execWorkspaceCommand(
|
||||
export function terminateWorkspaceCommand(commandId: string): boolean {
|
||||
return killProcess(commandId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 终止当前正在运行的 AI 工具命令
|
||||
* @returns 是否成功终止
|
||||
*/
|
||||
export function terminateCurrentToolCommand(): boolean {
|
||||
if (!_currentToolProcessId) return false;
|
||||
const id = _currentToolProcessId;
|
||||
_currentToolProcessId = null;
|
||||
return killProcess(id);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user