mirror of
https://github.com/geekgeekrun/geekgeekrun.git
synced 2026-08-28 19:46:50 +08:00
enhance daemon lifecycle logic like connect ensurance
This commit is contained in:
+28
-5
@@ -28,6 +28,9 @@ const net = require('net');
|
||||
const { spawn } = require('child_process');
|
||||
const path = require('path');
|
||||
const split2 = require('split2');
|
||||
const fs = require('fs')
|
||||
|
||||
const ipcWritePipe = fs.createWriteStream(null, { fd: 3 })
|
||||
|
||||
const PORT = 12345;
|
||||
const workers = new Map(); // workerId -> { process, status, restartCount, socket, latestScreenshot, latestScreenshotAt }
|
||||
@@ -88,6 +91,13 @@ const server = net.createServer((socket) => {
|
||||
function handleMessage(socket, message) {
|
||||
console.log('收到消息:', message);
|
||||
const _callbackUuid = message._callbackUuid
|
||||
if (message.type === 'ping') {
|
||||
sendResponse(socket, _callbackUuid, {
|
||||
success: true,
|
||||
message: 'pong'
|
||||
});
|
||||
return
|
||||
}
|
||||
|
||||
// 工具进程注册消息
|
||||
if (message.type === 'worker-register') {
|
||||
@@ -214,9 +224,8 @@ function handleMessage(socket, message) {
|
||||
|
||||
case 'get-status':
|
||||
const status = getWorkersStatus();
|
||||
sendResponse(socket, _callbackUuid, {
|
||||
sendResponse(socket, _callbackUuid, {
|
||||
success: true,
|
||||
type: 'status',
|
||||
workers: status
|
||||
});
|
||||
break;
|
||||
@@ -442,9 +451,23 @@ function sendResponse(socket, _callbackUuid, response) {
|
||||
}
|
||||
|
||||
// 启动服务器
|
||||
server.listen(PORT, () => {
|
||||
console.log(`守护进程服务器运行在端口 ${PORT}`);
|
||||
});
|
||||
new Promise((resolve, reject) => {
|
||||
server.once('error', (err) => {
|
||||
ipcWritePipe.write(
|
||||
JSON.stringify({ type: 'DAEMON_FATAL', error: err }),
|
||||
(err) => void err
|
||||
)
|
||||
reject(err)
|
||||
})
|
||||
server.listen(PORT, () => {
|
||||
console.log(`守护进程服务器运行在端口 ${PORT}`);
|
||||
ipcWritePipe.write(
|
||||
JSON.stringify({ type: 'DAEMON_READY' }),
|
||||
(err) => void err
|
||||
)
|
||||
resolve(true)
|
||||
});
|
||||
})
|
||||
|
||||
// 优雅关闭
|
||||
process.on('SIGTERM', () => {
|
||||
|
||||
@@ -1,244 +0,0 @@
|
||||
// 如果是通过 Electron 运行,禁用 GUI 和 Dock 图标
|
||||
if (typeof require !== 'undefined') {
|
||||
try {
|
||||
const { app } = require('electron');
|
||||
if (app) {
|
||||
// 隐藏 Dock 图标(macOS)
|
||||
if (process.platform === 'darwin') {
|
||||
app.dock?.hide();
|
||||
}
|
||||
|
||||
// 防止显示窗口
|
||||
app.on('ready', () => {
|
||||
// 不创建任何窗口,保持后台运行
|
||||
});
|
||||
|
||||
// 防止在没有窗口时退出
|
||||
app.on('window-all-closed', (e) => {
|
||||
e.preventDefault();
|
||||
// 不退出应用,保持后台运行
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
// 不在 Electron 环境中,忽略
|
||||
}
|
||||
}
|
||||
|
||||
// 工具进程示例
|
||||
const net = require('net');
|
||||
const split2 = require('split2');
|
||||
|
||||
// 解析命令行参数
|
||||
let workerId = 'unknown';
|
||||
let restartCount = 0;
|
||||
for (let i = 2; i < process.argv.length; i++) {
|
||||
const arg = process.argv[i];
|
||||
if (arg.startsWith('--worker-id=')) {
|
||||
workerId = arg.split('=')[1] || 'unknown';
|
||||
} else if (arg.startsWith('--restart-count=')) {
|
||||
restartCount = parseInt(arg.split('=')[1] || '0', 10);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`工具进程 ${workerId} 已启动 (PID: ${process.pid})${restartCount > 0 ? `,这是第${restartCount}次重启` : ''}`);
|
||||
|
||||
const DAEMON_PORT = 12345;
|
||||
let daemonSocket = null;
|
||||
let reconnectTimer = null;
|
||||
let isShuttingDown = false;
|
||||
|
||||
// 连接到守护进程
|
||||
function connectToDaemon() {
|
||||
if (isShuttingDown) return;
|
||||
|
||||
daemonSocket = new net.Socket();
|
||||
|
||||
daemonSocket.connect(DAEMON_PORT, 'localhost', () => {
|
||||
console.log(`[工具进程 ${workerId}] 已连接到守护进程`);
|
||||
|
||||
// 注册工具进程连接
|
||||
sendToDaemon({
|
||||
type: 'worker-register',
|
||||
workerId: workerId
|
||||
});
|
||||
|
||||
// 连接成功后立即检查是否应该退出
|
||||
setTimeout(() => {
|
||||
checkShouldIExit();
|
||||
}, 500);
|
||||
});
|
||||
|
||||
// 使用 split2 按行分割流式数据,处理 JSONL 格式(每行一个 JSON)
|
||||
// split2 会自动处理 TCP 分包问题,确保每条完整的消息(以换行符结尾)才会触发
|
||||
const splitStream = split2();
|
||||
|
||||
daemonSocket.pipe(splitStream).on('data', (line) => {
|
||||
const trimmedLine = line.toString().trim();
|
||||
if (!trimmedLine) {
|
||||
return; // 跳过空行
|
||||
}
|
||||
|
||||
try {
|
||||
const message = JSON.parse(trimmedLine);
|
||||
handleDaemonMessage(message);
|
||||
} catch (parseError) {
|
||||
console.error(`[工具进程 ${workerId}] 解析JSON消息失败:`, parseError.message);
|
||||
console.error('原始数据:', trimmedLine.substring(0, 100)); // 只打印前100个字符
|
||||
}
|
||||
});
|
||||
|
||||
splitStream.on('error', (err) => {
|
||||
console.error(`[工具进程 ${workerId}] split2 流处理错误:`, err);
|
||||
});
|
||||
|
||||
daemonSocket.on('error', (err) => {
|
||||
console.error(`[工具进程 ${workerId}] 守护进程连接错误:`, err.message);
|
||||
daemonSocket = null;
|
||||
|
||||
// 尝试重连(如果不是正在关闭)
|
||||
if (!isShuttingDown) {
|
||||
if (reconnectTimer) clearTimeout(reconnectTimer);
|
||||
reconnectTimer = setTimeout(() => {
|
||||
console.log(`[工具进程 ${workerId}] 尝试重新连接守护进程...`);
|
||||
connectToDaemon();
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
|
||||
daemonSocket.on('close', () => {
|
||||
console.log(`[工具进程 ${workerId}] 守护进程连接已关闭`);
|
||||
daemonSocket = null;
|
||||
|
||||
// 尝试重连(如果不是正在关闭)
|
||||
if (!isShuttingDown) {
|
||||
if (reconnectTimer) clearTimeout(reconnectTimer);
|
||||
reconnectTimer = setTimeout(() => {
|
||||
console.log(`[工具进程 ${workerId}] 尝试重新连接守护进程...`);
|
||||
connectToDaemon();
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 发送消息到守护进程
|
||||
function sendToDaemon(message) {
|
||||
if (daemonSocket && !daemonSocket.destroyed) {
|
||||
try {
|
||||
daemonSocket.write(JSON.stringify(message) + '\n');
|
||||
} catch (e) {
|
||||
console.error(`[工具进程 ${workerId}] 发送消息失败:`, e);
|
||||
}
|
||||
} else {
|
||||
console.warn(`[工具进程 ${workerId}] 守护进程未连接,消息已丢弃:`, message.type);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理守护进程消息
|
||||
function handleDaemonMessage(message) {
|
||||
if (message.type === 'worker-registered') {
|
||||
console.log(`[工具进程 ${workerId}] 连接已注册到守护进程`);
|
||||
} else if (message.type === 'check-should-exit') {
|
||||
// 处理是否应该退出的查询响应
|
||||
if (message.shouldExit) {
|
||||
console.log(`[工具进程 ${workerId}] 守护进程指示应该退出,正在退出...`);
|
||||
shutdown();
|
||||
}
|
||||
} else if (message.error) {
|
||||
console.error(`[工具进程 ${workerId}] 守护进程错误:`, message.error);
|
||||
// 如果守护进程要求退出(例如:已被标记为停止)
|
||||
if (message.shouldExit) {
|
||||
console.log(`[工具进程 ${workerId}] 守护进程要求退出,正在退出...`);
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否应该退出
|
||||
function checkShouldIExit() {
|
||||
if (isShuttingDown) return;
|
||||
|
||||
if (!daemonSocket || daemonSocket.destroyed) {
|
||||
// 如果守护进程未连接,暂时不检查,等待重连
|
||||
return;
|
||||
}
|
||||
|
||||
sendToDaemon({
|
||||
type: 'check-should-exit',
|
||||
workerId: workerId
|
||||
});
|
||||
}
|
||||
|
||||
// 初始连接
|
||||
connectToDaemon();
|
||||
|
||||
// 模拟工作负载
|
||||
let counter = 0;
|
||||
const interval = setInterval(() => {
|
||||
counter++;
|
||||
console.log(`[工具进程 ${workerId}] 运行中... 计数: ${counter}`);
|
||||
|
||||
// 发送工作数据到守护进程
|
||||
sendToDaemon({
|
||||
type: 'worker-data',
|
||||
workerId: workerId,
|
||||
data: {
|
||||
counter: counter,
|
||||
timestamp: Date.now(),
|
||||
message: `工具进程 ${workerId} 工作数据`
|
||||
}
|
||||
});
|
||||
|
||||
// 模拟随机错误(用于测试自动重启功能)
|
||||
// 取消下面的注释来测试自动重启
|
||||
// if (Math.random() < 0.001) {
|
||||
// console.error(`[工具进程 ${workerId}] 模拟错误,退出`);
|
||||
// process.exit(1);
|
||||
// }
|
||||
}, 2000);
|
||||
|
||||
// 处理退出信号
|
||||
function shutdown() {
|
||||
if (isShuttingDown) return;
|
||||
isShuttingDown = true;
|
||||
|
||||
clearInterval(interval);
|
||||
if (reconnectTimer) clearTimeout(reconnectTimer);
|
||||
|
||||
if (daemonSocket && !daemonSocket.destroyed) {
|
||||
daemonSocket.destroy();
|
||||
}
|
||||
|
||||
console.log(`[工具进程 ${workerId}] 正在退出...`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
process.on('SIGTERM', () => {
|
||||
console.log(`[工具进程 ${workerId}] 收到SIGTERM信号,正在退出...`);
|
||||
shutdown();
|
||||
});
|
||||
|
||||
process.on('SIGINT', () => {
|
||||
console.log(`[工具进程 ${workerId}] 收到SIGINT信号,正在退出...`);
|
||||
shutdown();
|
||||
});
|
||||
|
||||
// 处理未捕获的异常
|
||||
process.on('uncaughtException', (error) => {
|
||||
console.error(`[工具进程 ${workerId}] 未捕获的异常:`, error);
|
||||
clearInterval(interval);
|
||||
if (daemonSocket && !daemonSocket.destroyed) {
|
||||
daemonSocket.destroy();
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
// 定期发送心跳到守护进程,并检查是否应该退出
|
||||
setInterval(() => {
|
||||
sendToDaemon({
|
||||
type: 'worker-heartbeat',
|
||||
workerId: workerId,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
// 每次心跳时也检查是否应该退出
|
||||
checkShouldIExit();
|
||||
}, 5000);
|
||||
@@ -5,8 +5,6 @@ import {
|
||||
readConfigFile,
|
||||
getPublicDbFilePath
|
||||
} from '@geekgeekrun/geek-auto-start-chat-with-boss/runtime-file-utils.mjs'
|
||||
|
||||
import * as fs from 'fs'
|
||||
// import { pipeWriteRegardlessError } from '../utils/pipe'
|
||||
import { getAnyAvailablePuppeteerExecutable } from '../CHECK_AND_DOWNLOAD_DEPENDENCIES/utils/puppeteer-executable'
|
||||
import { sleep } from '@geekgeekrun/utils/sleep.mjs'
|
||||
@@ -16,7 +14,7 @@ import attachListenerForKillSelfOnParentExited from '../../utils/attachListenerF
|
||||
import SqlitePluginModule from '@geekgeekrun/sqlite-plugin'
|
||||
import gtag from '../../utils/gtag'
|
||||
import GtagPlugin from '../../utils/gtag/GtagPlugin'
|
||||
import { connectToDaemon } from '../OPEN_SETTING_WINDOW/connect-to-daemon'
|
||||
import { connectToDaemon, sendToDaemon } from '../OPEN_SETTING_WINDOW/connect-to-daemon'
|
||||
import { PeriodPushCurrentPageScreenshotPlugin } from '../../utils/screenshot'
|
||||
import { checkShouldExit } from '../../utils/worker'
|
||||
const { default: SqlitePlugin } = SqlitePluginModule
|
||||
@@ -48,26 +46,8 @@ const runAutoChat = async () => {
|
||||
app.exit()
|
||||
})
|
||||
app.dock?.hide()
|
||||
let pipe: null | fs.WriteStream = null
|
||||
try {
|
||||
pipe = fs.createWriteStream(null, { fd: 3 })
|
||||
} catch {
|
||||
console.warn('pipe is not available')
|
||||
}
|
||||
// pipeWriteRegardlessError(
|
||||
// pipe,
|
||||
// JSON.stringify({
|
||||
// type: 'INITIALIZE_PUPPETEER'
|
||||
// }) + '\r\n'
|
||||
// )
|
||||
try {
|
||||
await initPuppeteer()
|
||||
// pipeWriteRegardlessError(
|
||||
// pipe,
|
||||
// JSON.stringify({
|
||||
// type: 'PUPPETEER_INITIALIZE_SUCCESSFULLY'
|
||||
// }) + '\r\n'
|
||||
// )
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
app.exit(AUTO_CHAT_ERROR_EXIT_CODE.PUPPETEER_IS_NOT_EXECUTABLE)
|
||||
@@ -101,20 +81,8 @@ const runAutoChat = async () => {
|
||||
initPlugins(hooks)
|
||||
|
||||
gtag('run_auto_chat_with_boss_main_ready')
|
||||
// pipeWriteRegardlessError(
|
||||
// pipe,
|
||||
// JSON.stringify({
|
||||
// type: 'GEEK_AUTO_START_CHAT_WITH_BOSS_STARTED' //geek-auto-start-chat-with-boss-started
|
||||
// }) + '\r\n'
|
||||
// )
|
||||
|
||||
autoStartChatEventBus.once('LOGIN_STATUS_INVALID', () => {
|
||||
// pipeWriteRegardlessError(
|
||||
// pipe,
|
||||
// JSON.stringify({
|
||||
// type: 'LOGIN_STATUS_INVALID' //geek-auto-start-chat-with-boss-started
|
||||
// }) + '\r\n'
|
||||
// )
|
||||
})
|
||||
|
||||
while (true) {
|
||||
@@ -159,15 +127,16 @@ const runAutoChat = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
export const waitForProcessHandShakeAndRunAutoChat = () => {
|
||||
// let pipe: null | fs.WriteStream = null
|
||||
// try {
|
||||
// pipe = fs.createWriteStream(null, { fd: 3 })
|
||||
// } catch {
|
||||
// console.error('pipe is not available')
|
||||
// app.exit(1)
|
||||
// }
|
||||
connectToDaemon()
|
||||
export const waitForProcessHandShakeAndRunAutoChat = async () => {
|
||||
await connectToDaemon()
|
||||
await sendToDaemon(
|
||||
{
|
||||
type: 'ping'
|
||||
},
|
||||
{
|
||||
needCallback: true
|
||||
}
|
||||
)
|
||||
runAutoChat()
|
||||
}
|
||||
|
||||
|
||||
@@ -21,12 +21,6 @@ export const launchBossZhipinLoginPageWithPreloadExtension = async () => {
|
||||
const { initPuppeteer } = await import('@geekgeekrun/geek-auto-start-chat-with-boss/index.mjs')
|
||||
try {
|
||||
await initPuppeteer()
|
||||
pipeWriteRegardlessError(
|
||||
pipe,
|
||||
JSON.stringify({
|
||||
type: 'PUPPETEER_INITIALIZE_SUCCESSFULLY'
|
||||
}) + '\r\n'
|
||||
)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
app.exit(1)
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import attachListenerForKillSelfOnParentExited from '../utils/attachListenerForKillSelfOnParentExited'
|
||||
;(async () => {
|
||||
process.once('disconnect', () => {
|
||||
process.exit(0)
|
||||
})
|
||||
attachListenerForKillSelfOnParentExited()
|
||||
await import('@geekgeekrun/pm/daemon.js')
|
||||
})()
|
||||
|
||||
|
||||
@@ -11,76 +11,79 @@ export const daemonEE = new EventEmitter()
|
||||
const waitForCallbackTaskMap = new Map()
|
||||
|
||||
// 连接到守护进程
|
||||
export function connectToDaemon() {
|
||||
export async function connectToDaemon() {
|
||||
daemonClient = new net.Socket();
|
||||
daemonClient.connect(DAEMON_PORT, 'localhost', () => {
|
||||
console.log('已连接到守护进程');
|
||||
daemonEE.emit('connect')
|
||||
// 通知渲染进程连接成功
|
||||
// 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);
|
||||
daemonEE.emit('message', message)
|
||||
// FIXME:
|
||||
console.log('收到守护进程消息:', message);
|
||||
if (
|
||||
message._callbackUuid
|
||||
) {
|
||||
const callbackInfo = waitForCallbackTaskMap.get(message._callbackUuid)
|
||||
if (callbackInfo) {
|
||||
const isError = message._isError
|
||||
if (isError) {
|
||||
callbackInfo.reject(message)
|
||||
}
|
||||
else {
|
||||
callbackInfo.resolve(message)
|
||||
}
|
||||
waitForCallbackTaskMap.delete(message._callbackUuid)
|
||||
let isConnected = false
|
||||
await new Promise((resolve, reject) => {
|
||||
daemonClient.connect(DAEMON_PORT, 'localhost', () => {
|
||||
isConnected = true
|
||||
console.log('已连接到守护进程');
|
||||
daemonEE.emit('connect')
|
||||
// 使用 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);
|
||||
daemonEE.emit('message', message)
|
||||
// FIXME:
|
||||
console.log('收到守护进程消息:', message);
|
||||
if (message._callbackUuid) {
|
||||
const callbackInfo = waitForCallbackTaskMap.get(message._callbackUuid)
|
||||
if (callbackInfo) {
|
||||
const isError = message._isError
|
||||
if (isError) {
|
||||
callbackInfo.reject(message)
|
||||
} else {
|
||||
callbackInfo.resolve(message)
|
||||
}
|
||||
waitForCallbackTaskMap.delete(message._callbackUuid)
|
||||
}
|
||||
}
|
||||
// 转发消息到渲染进程
|
||||
// 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('close', () => {
|
||||
if (!isConnected) {
|
||||
return
|
||||
}
|
||||
console.log('守护进程连接已关闭');
|
||||
daemonEE.emit('close')
|
||||
});
|
||||
|
||||
resolve(true)
|
||||
// 通知渲染进程连接成功
|
||||
// if (mainWindow) {
|
||||
// mainWindow.webContents.send('daemon-message', message);
|
||||
// mainWindow.webContents.send('daemon-connected');
|
||||
// }
|
||||
} 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);
|
||||
daemonEE.emit('error', err)
|
||||
// 尝试重连
|
||||
setTimeout(() => {
|
||||
if (daemonClient.destroyed) {
|
||||
connectToDaemon();
|
||||
daemonClient.on('close', () => {
|
||||
if (isConnected) {
|
||||
return
|
||||
}
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
daemonClient.on('close', () => {
|
||||
console.log('守护进程连接已关闭');
|
||||
daemonEE.emit('close')
|
||||
// 尝试重连
|
||||
setTimeout(() => {
|
||||
connectToDaemon();
|
||||
}, 2000);
|
||||
});
|
||||
reject(new Error('连接到守护进程超时'))
|
||||
});
|
||||
daemonClient.on('error', (err) => {
|
||||
console.error('守护进程连接错误:', err);
|
||||
daemonEE.emit('error', err)
|
||||
reject(err)
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
// 向守护进程发送消息
|
||||
|
||||
@@ -6,8 +6,9 @@ import initIpc from './ipc'
|
||||
import gtag from '../../utils/gtag'
|
||||
import initPublicIpc from '../../utils/initPublicIpc'
|
||||
import { launchDaemon } from './launch-daemon'
|
||||
import { connectToDaemon } from './connect-to-daemon'
|
||||
import { sleep } from '@geekgeekrun/utils/sleep.mjs'
|
||||
import { connectToDaemon, sendToDaemon } from './connect-to-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()) {
|
||||
@@ -71,8 +72,15 @@ export function openSettingWindow() {
|
||||
|
||||
whenReadyPromise.then(async () => {
|
||||
await launchDaemon()
|
||||
// FIXME:
|
||||
await sleep(2000)
|
||||
await connectToDaemon()
|
||||
await sendToDaemon(
|
||||
{
|
||||
type: 'ping'
|
||||
},
|
||||
{
|
||||
needCallback: true
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { connectToDaemon } from "./connect-to-daemon";
|
||||
|
||||
const { app } = require('electron');
|
||||
const { spawn } = require('child_process');
|
||||
|
||||
@@ -27,7 +25,7 @@ export function launchDaemon() {
|
||||
});
|
||||
|
||||
// 启动守护进程
|
||||
function startDaemon() {
|
||||
async function startDaemon() {
|
||||
console.log('启动守护进程...');
|
||||
// 使用 Electron 可执行程序路径,如果没有则回退到 node
|
||||
const electronPath = process.execPath;
|
||||
@@ -40,7 +38,7 @@ export function launchDaemon() {
|
||||
? [process.argv[1], `--mode=launchDaemon`]
|
||||
: [`--mode=launchDaemon`],
|
||||
{
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
stdio: ['ignore', 'pipe', 'pipe', 'pipe'],
|
||||
detached: false,
|
||||
env: {
|
||||
...process.env,
|
||||
@@ -69,26 +67,26 @@ export function launchDaemon() {
|
||||
console.error(`守护进程错误: ${data}`);
|
||||
});
|
||||
|
||||
daemonProcess.on('exit', (code) => {
|
||||
console.log(`守护进程退出,代码: ${code}`);
|
||||
// 如果守护进程意外退出,尝试重启
|
||||
if (code !== 0) {
|
||||
setTimeout(() => {
|
||||
console.log('尝试重启守护进程...');
|
||||
startDaemon();
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
|
||||
// 等待守护进程启动后连接
|
||||
setTimeout(() => {
|
||||
connectToDaemon();
|
||||
}, 1000);
|
||||
return new Promise((resolve, reject) => {
|
||||
daemonProcess.stdio[3].on('data', (rawData) => {
|
||||
let data
|
||||
try {
|
||||
data = JSON.parse(rawData.toString())
|
||||
if (data.type === 'DAEMON_READY') {
|
||||
resolve(true)
|
||||
}
|
||||
else if (data.type === 'DAEMON_FATAL') {
|
||||
reject(new Error(data.error))
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
console.error('', err)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 应用准备就绪
|
||||
return app.whenReady().then(() => {
|
||||
startDaemon();
|
||||
});
|
||||
return app.whenReady().then(() => startDaemon());
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import gtag from '../../utils/gtag'
|
||||
import { JobHireStatus } from '@geekgeekrun/sqlite-plugin/dist/enums';
|
||||
import dayjs from 'dayjs'
|
||||
import cheerio from 'cheerio'
|
||||
import { connectToDaemon } from '../OPEN_SETTING_WINDOW/connect-to-daemon'
|
||||
import { connectToDaemon, sendToDaemon } from '../OPEN_SETTING_WINDOW/connect-to-daemon'
|
||||
import { pushCurrentPageScreenshot, SCREENSHOT_INTERVAL_MS } from '../../utils/screenshot'
|
||||
import { checkShouldExit } from '../../utils/worker'
|
||||
|
||||
@@ -464,8 +464,13 @@ const rerunInterval = (() => {
|
||||
})()
|
||||
|
||||
export async function runEntry() {
|
||||
connectToDaemon()
|
||||
app.dock?.hide()
|
||||
await connectToDaemon()
|
||||
await sendToDaemon({
|
||||
type: 'ping'
|
||||
}, {
|
||||
needCallback: true
|
||||
})
|
||||
while (true) {
|
||||
try {
|
||||
await mainLoop()
|
||||
|
||||
Reference in New Issue
Block a user