mirror of
https://github.com/geekgeekrun/geekgeekrun.git
synced 2026-05-06 20:02:47 +08:00
launch daemon in main process
This commit is contained in:
@@ -35,6 +35,7 @@
|
||||
"@geekgeekrun/launch-bosszhipin-login-page-with-preload-extension": "workspace:*",
|
||||
"@geekgeekrun/sqlite-plugin": "workspace:*",
|
||||
"@geekgeekrun/utils": "workspace:*",
|
||||
"@geekgeekrun/pm": "workspace:*",
|
||||
"JSONStream": "^1.3.5",
|
||||
"diff": "^7.0.0",
|
||||
"electron-updater": "^6.1.7",
|
||||
|
||||
5
packages/ui/src/main/flow/LAUNCH_DAEMON.ts
Normal file
5
packages/ui/src/main/flow/LAUNCH_DAEMON.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
;(async () => {
|
||||
await import('@geekgeekrun/pm/daemon.js')
|
||||
})()
|
||||
|
||||
export {}
|
||||
194
packages/ui/src/main/flow/OPEN_SETTING_WINDOW/attach-daemon.ts
Normal file
194
packages/ui/src/main/flow/OPEN_SETTING_WINDOW/attach-daemon.ts
Normal file
@@ -0,0 +1,194 @@
|
||||
const { app, ipcMain } = require('electron');
|
||||
const path = require('path');
|
||||
const { spawn } = require('child_process');
|
||||
const net = require('net');
|
||||
const split2 = require('split2');
|
||||
|
||||
const isUiDev = process.env.NODE_ENV === 'development'
|
||||
const DAEMON_PORT = 12345;
|
||||
|
||||
export function launchDaemon() {
|
||||
let daemonProcess = null;
|
||||
|
||||
// 所有窗口关闭时
|
||||
app.on('window-all-closed', () => {
|
||||
// if (process.platform !== 'darwin') {
|
||||
// 关闭守护进程
|
||||
if (daemonProcess) {
|
||||
daemonProcess.kill();
|
||||
}
|
||||
// app.quit();
|
||||
// }
|
||||
});
|
||||
|
||||
// 应用退出前清理
|
||||
app.on('before-quit', () => {
|
||||
if (daemonProcess) {
|
||||
daemonProcess.kill();
|
||||
}
|
||||
});
|
||||
|
||||
// 启动守护进程
|
||||
function startDaemon() {
|
||||
console.log('启动守护进程...');
|
||||
// 使用 Electron 可执行程序路径,如果没有则回退到 node
|
||||
const electronPath = process.execPath;
|
||||
console.log(`使用 Electron 路径: ${electronPath}`);
|
||||
|
||||
// 添加参数使守护进程在后台运行,不显示 UI
|
||||
daemonProcess = spawn(
|
||||
process.argv[0],
|
||||
isUiDev
|
||||
? [process.argv[1], `--mode=launchDaemon`]
|
||||
: [`--mode=launchDaemon`],
|
||||
{
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
detached: false,
|
||||
env: {
|
||||
...process.env,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// daemonProcess = spawn(electronPath, [
|
||||
// '--no-sandbox',
|
||||
// '--disable-dev-shm-usage',
|
||||
// path.join(__dirname, 'daemon.js')
|
||||
// ], {
|
||||
// stdio: ['ignore', 'pipe', 'pipe'],
|
||||
// detached: false,
|
||||
// env: {
|
||||
// ...process.env,
|
||||
// ELECTRON_EXEC_PATH: electronPath // 传递给守护进程,用于启动 worker
|
||||
// }
|
||||
// });
|
||||
|
||||
daemonProcess.stdout.on('data', (data) => {
|
||||
console.log(`守护进程输出: ${data}`);
|
||||
});
|
||||
|
||||
daemonProcess.stderr.on('data', (data) => {
|
||||
console.error(`守护进程错误: ${data}`);
|
||||
});
|
||||
|
||||
daemonProcess.on('exit', (code) => {
|
||||
console.log(`守护进程退出,代码: ${code}`);
|
||||
// 如果守护进程意外退出,尝试重启
|
||||
if (code !== 0) {
|
||||
setTimeout(() => {
|
||||
console.log('尝试重启守护进程...');
|
||||
startDaemon();
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
|
||||
// 等待守护进程启动后连接
|
||||
setTimeout(() => {
|
||||
connectToDaemon();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// 应用准备就绪
|
||||
return app.whenReady().then(() => {
|
||||
startDaemon();
|
||||
});
|
||||
}
|
||||
|
||||
export function connectToDaemon() {
|
||||
let daemonClient = null;
|
||||
// 连接到守护进程
|
||||
function _connectToDaemon() {
|
||||
daemonClient = new net.Socket();
|
||||
daemonClient.connect(DAEMON_PORT, 'localhost', () => {
|
||||
console.log('已连接到守护进程');
|
||||
// 通知渲染进程连接成功
|
||||
// if (mainWindow) {
|
||||
// mainWindow.webContents.send('daemon-connected');
|
||||
// }
|
||||
});
|
||||
// 使用 split2 按行分割流式数据,处理 JSONL 格式(每行一个 JSON)
|
||||
const splitStream = split2();
|
||||
daemonClient.pipe(splitStream).on('data', (line) => {
|
||||
const trimmedLine = line.toString().trim();
|
||||
if (!trimmedLine) {
|
||||
return; // 跳过空行
|
||||
}
|
||||
try {
|
||||
const message = JSON.parse(trimmedLine);
|
||||
console.log('收到守护进程消息:', message);
|
||||
// 转发消息到渲染进程
|
||||
// if (mainWindow) {
|
||||
// mainWindow.webContents.send('daemon-message', message);
|
||||
// }
|
||||
} catch (parseError) {
|
||||
console.error('解析守护进程消息失败:', parseError.message);
|
||||
console.error('原始数据:', trimmedLine.substring(0, 100));
|
||||
}
|
||||
});
|
||||
|
||||
splitStream.on('error', (err) => {
|
||||
console.error('split2 流处理错误:', err);
|
||||
});
|
||||
|
||||
daemonClient.on('error', (err) => {
|
||||
console.error('守护进程连接错误:', err);
|
||||
// 尝试重连
|
||||
setTimeout(() => {
|
||||
if (daemonClient.destroyed) {
|
||||
_connectToDaemon();
|
||||
}
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
daemonClient.on('close', () => {
|
||||
console.log('守护进程连接已关闭');
|
||||
// 尝试重连
|
||||
setTimeout(() => {
|
||||
_connectToDaemon();
|
||||
}, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
// 向守护进程发送消息
|
||||
function sendToDaemon(message) {
|
||||
if (daemonClient && !daemonClient.destroyed) {
|
||||
daemonClient.write(JSON.stringify(message) + '\n');
|
||||
} else {
|
||||
console.error('守护进程未连接');
|
||||
}
|
||||
}
|
||||
|
||||
// IPC处理:从渲染进程接收消息并转发到守护进程
|
||||
ipcMain.on('send-to-daemon', (event, message) => {
|
||||
sendToDaemon(message);
|
||||
});
|
||||
|
||||
// IPC处理:启动工具进程
|
||||
ipcMain.on('start-worker', (event, workerId) => {
|
||||
sendToDaemon({ type: 'start-worker', workerId });
|
||||
});
|
||||
|
||||
// IPC处理:停止工具进程
|
||||
ipcMain.on('stop-worker', (event, workerId) => {
|
||||
sendToDaemon({ type: 'stop-worker', workerId });
|
||||
});
|
||||
|
||||
// IPC处理:获取所有工具进程状态
|
||||
ipcMain.on('get-workers-status', () => {
|
||||
sendToDaemon({ type: 'get-status' });
|
||||
});
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (daemonClient) {
|
||||
daemonClient.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
app.on('before-quit', () => {
|
||||
if (daemonClient) {
|
||||
daemonClient.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
_connectToDaemon()
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import './app-menu'
|
||||
import initIpc from './ipc'
|
||||
import gtag from '../../utils/gtag'
|
||||
import initPublicIpc from '../../utils/initPublicIpc'
|
||||
import { connectToDaemon, launchDaemon } from './attach-daemon'
|
||||
import { sleep } from '@geekgeekrun/utils/sleep.mjs'
|
||||
export function openSettingWindow() {
|
||||
// TODO: singleton lock; how can we check if there is another process should run as singleton with arguments?
|
||||
if (!app.requestSingleInstanceLock()) {
|
||||
@@ -65,4 +67,11 @@ export function openSettingWindow() {
|
||||
globalShortcut.unregister('Command+Option+Shift+/')
|
||||
})
|
||||
})
|
||||
|
||||
whenReadyPromise.then(async () => {
|
||||
await launchDaemon()
|
||||
// FIXME:
|
||||
await sleep(2000)
|
||||
await connectToDaemon()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -45,6 +45,10 @@ const runMode = commandlineArgs['mode'];
|
||||
runEntry()
|
||||
break
|
||||
}
|
||||
case 'launchDaemon': {
|
||||
await import('./flow/LAUNCH_DAEMON')
|
||||
break
|
||||
}
|
||||
default: {
|
||||
const { openSettingWindow } = await import('./flow/OPEN_SETTING_WINDOW/index')
|
||||
openSettingWindow()
|
||||
|
||||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@@ -130,6 +130,9 @@ importers:
|
||||
'@geekgeekrun/launch-bosszhipin-login-page-with-preload-extension':
|
||||
specifier: workspace:*
|
||||
version: link:../launch-bosszhipin-login-page-with-preload-extension
|
||||
'@geekgeekrun/pm':
|
||||
specifier: workspace:*
|
||||
version: link:../pm
|
||||
'@geekgeekrun/sqlite-plugin':
|
||||
specifier: workspace:*
|
||||
version: link:../sqlite-plugin
|
||||
|
||||
Reference in New Issue
Block a user