基本实现通用自动化(含图表、代码、ppt等生成)

This commit is contained in:
minorQAQ committed 2026-07-14 19:22:08 +08:00
1 parent 1111d208c4
commit b422e96b2c
197 files changed
+13923 -8081

No files matched your search

+194
View File
@@ -0,0 +1,194 @@
// main.js — Electron 主进程
// 功能:启动 FastAPI 后端 → 等待就绪 → 加载 Web UI → 管理生命周期
const { app, BrowserWindow, dialog } = require('electron');
const { spawn } = require('child_process');
const path = require('path');
const http = require('http');
const fs = require('fs');
// ---------- 路径配置 ----------
const AGENT_DIR = path.join(__dirname, '..'); // Agent/ 目录
const VENV_PYTHON = path.join(AGENT_DIR, 'myenv', 'Scripts', 'python.exe');
const HOST = '127.0.0.1';
const PORT = 8765;
const BASE_URL = `http://${HOST}:${PORT}`;
// ---------- 全局状态 ----------
let mainWindow = null;
let pythonProcess = null;
// ---------- FastAPI 生命周期 ----------
function startFastAPI() {
return new Promise((resolve, reject) => {
// 如果找不到虚拟环境 Python,尝试使用系统 Python
let pythonExe = VENV_PYTHON;
if (!fs.existsSync(pythonExe)) {
pythonExe = 'python'; // 回退到 PATH 中的 Python
console.log('[Electron] 未找到 venv Python,使用系统 Python');
}
console.log(`[Electron] Python: ${pythonExe}`);
console.log(`[Electron] Agent 目录: ${AGENT_DIR}`);
pythonProcess = spawn(pythonExe, [
'-m', 'uvicorn',
'web.server:app',
'--host', HOST,
'--port', String(PORT),
'--log-level', 'warning',
], {
cwd: AGENT_DIR,
env: {
...process.env,
PYTHONPATH: 'src',
PYTHONUNBUFFERED: '1',
},
shell: true, // Windows 需要 shell 以正确解析路径
stdio: ['ignore', 'pipe', 'pipe'],
});
pythonProcess.stdout.on('data', (data) => {
console.log(`[FastAPI] ${data.toString().trim()}`);
});
pythonProcess.stderr.on('data', (data) => {
console.log(`[FastAPI] ${data.toString().trim()}`);
});
pythonProcess.on('error', (err) => {
console.error('[Electron] 启动 FastAPI 失败:', err.message);
reject(err);
});
pythonProcess.on('exit', (code) => {
console.log(`[Electron] FastAPI 进程退出,退出码: ${code}`);
pythonProcess = null;
// 非正常退出时提醒用户
if (code !== 0 && code !== null && mainWindow) {
dialog.showErrorBox(
'后端服务异常',
`FastAPI 服务意外退出(退出码: ${code})。\n请检查终端输出了解详情。`
);
}
});
// 轮询等待服务就绪
waitForReady(resolve, reject, 30); // 最多等 30 秒
});
}
function waitForReady(resolve, reject, retriesLeft) {
if (retriesLeft <= 0) {
reject(new Error('FastAPI 启动超时,请检查依赖是否安装完整'));
return;
}
const req = http.get(`${BASE_URL}/api/health`, (res) => {
if (res.statusCode === 200) {
console.log('[Electron] FastAPI 服务就绪');
resolve();
} else {
retry();
}
});
req.on('error', () => retry());
req.setTimeout(2000, () => { req.destroy(); retry(); });
function retry() {
setTimeout(() => waitForReady(resolve, reject, retriesLeft - 1), 1000);
}
}
function stopFastAPI() {
if (pythonProcess) {
console.log('[Electron] 正在关闭 FastAPI...');
// Windows 上 kill 整个进程树
if (process.platform === 'win32') {
spawn('taskkill', ['/pid', String(pythonProcess.pid), '/f', '/t']);
} else {
pythonProcess.kill('SIGTERM');
}
pythonProcess = null;
}
}
// ---------- 窗口管理 ----------
function createWindow() {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
minWidth: 800,
minHeight: 600,
title: 'minor Agent',
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
},
show: false, // 等 ready-to-show 再显示,避免白屏
});
mainWindow.loadURL(BASE_URL);
mainWindow.once('ready-to-show', () => {
mainWindow.show();
});
mainWindow.on('closed', () => {
mainWindow = null;
});
// 在默认浏览器打开外部链接
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
require('electron').shell.openExternal(url);
return { action: 'deny' };
});
}
// ---------- 应用生命周期 ----------
app.whenReady().then(async () => {
try {
await startFastAPI();
createWindow();
} catch (err) {
dialog.showErrorBox(
'启动失败',
`无法启动 minor Agent:\n${err.message}\n\n` +
'请确保已安装依赖,或通过命令行运行:\n' +
`cd "${AGENT_DIR}"\n` +
'set PYTHONPATH=src\n' +
'python -m uvicorn web.server:app --host 127.0.0.1 --port 8765'
);
app.quit();
}
});
app.on('window-all-closed', () => {
stopFastAPI();
app.quit();
});
app.on('before-quit', () => {
stopFastAPI();
});
// macOS: 点击 Dock 图标重新创建窗口
app.on('activate', () => {
if (mainWindow === null) {
createWindow();
}
});
// 防止多实例(可选)
const gotLock = app.requestSingleInstanceLock();
if (!gotLock) {
app.quit();
} else {
app.on('second-instance', () => {
if (mainWindow) {
if (mainWindow.isMinimized()) mainWindow.restore();
mainWindow.focus();
}
});
}