基本实现通用自动化(含图表、代码、ppt等生成)
This commit is contained in:
1 parent
1111d208c4
commit
b422e96b2c
197 files changed
+13923
-8081
No files matched your search
@@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user