mirror of
https://github.com/geekgeekrun/geekgeekrun.git
synced 2026-05-11 18:09:50 +08:00
make task can be run standalone via arg
This commit is contained in:
@@ -30,6 +30,7 @@ const path = require('path');
|
||||
const split2 = require('split2');
|
||||
const fs = require('fs')
|
||||
const { tmpdir } = require('os')
|
||||
const { randomUUID } = require('crypto')
|
||||
|
||||
const ipcWritePipe = fs.createWriteStream(null, { fd: 3 })
|
||||
let ipcSocketName = process.env.GEEKGEEKRUND_PIPE_NAME
|
||||
@@ -43,7 +44,9 @@ if (process.platform === 'win32') {
|
||||
}
|
||||
else {
|
||||
ipcSocketPath = path.join(tmpdir(), `${ipcSocketName}.sock`)
|
||||
fs.writeFileSync(ipcSocketPath, '')
|
||||
if (!fs.existsSync(ipcSocketPath)) {
|
||||
fs.writeFileSync(ipcSocketPath, '')
|
||||
}
|
||||
// 设置权限(Unix)
|
||||
fs.chmodSync(
|
||||
ipcSocketPath,
|
||||
@@ -259,11 +262,11 @@ function startWorker({ workerId, command, args, env }, restartCount = 0) {
|
||||
console.log(`工具进程 ${workerId} 已在运行`);
|
||||
return;
|
||||
}
|
||||
const noRestartExitCodeSet = new Set([0]);
|
||||
(env.GEEKGEEKRUND_NO_RESTART_EXIT_CODE ?? '')
|
||||
const noAutoRestartExitCodeSet = new Set([0]);
|
||||
(env.GEEKGEEKRUND_NO_AUTO_RESTART_EXIT_CODE ?? '')
|
||||
.split(',')
|
||||
.map(n => parseInt(n))
|
||||
.forEach(n => noRestartExitCodeSet.add(n))
|
||||
.forEach(n => noAutoRestartExitCodeSet.add(n))
|
||||
|
||||
console.log(`启动工具进程: ${workerId}${restartCount > 0 ? ` (重启第${restartCount}次)` : ''}`);
|
||||
// 添加参数使工具进程在后台运行,不显示 UI
|
||||
@@ -297,7 +300,7 @@ function startWorker({ workerId, command, args, env }, restartCount = 0) {
|
||||
workerInfo.socket.destroy();
|
||||
}
|
||||
|
||||
const shouldRestart = !noRestartExitCodeSet.has(code) // && code !== null;
|
||||
const shouldRestart = !noAutoRestartExitCodeSet.has(code) // && code !== null;
|
||||
// 使用当前的 restartCount 加1,而不是从 workerInfo 中取(因为可能已经被删除)
|
||||
const restartCount = (workerInfo.restartCount || 0) + 1;
|
||||
|
||||
@@ -509,3 +512,11 @@ process.on('SIGINT', () => {
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
|
||||
// 捕获未处理的 EPIPE 错误
|
||||
process.on('uncaughtException', (err) => {
|
||||
if (err.code === 'EPIPE' || err.code === 'ERR_STREAM_DESTROYED') {
|
||||
return
|
||||
}
|
||||
throw err
|
||||
});
|
||||
@@ -9,10 +9,6 @@
|
||||
"scripts": {
|
||||
"start": "node scripts/run-build-sqlite-plugin.mjs && electron-vite preview",
|
||||
"dev": "node scripts/run-build-sqlite-plugin.mjs && electron-vite dev",
|
||||
"dev:geek-auto-start-chat-with-boss-main-only": "cross-env MAIN_BOSSGEEKGO_UI_RUN_MODE=geekAutoStartWithBossMain pnpm run dev",
|
||||
"dev:geek-auto-start-chat-with-boss-daemon-only": "cross-env MAIN_BOSSGEEKGO_UI_RUN_MODE=geekAutoStartWithBossDaemon pnpm run dev",
|
||||
"dev:check-and-download-dependencies-for-init-only": "cross-env MAIN_BOSSGEEKGO_UI_RUN_MODE=checkAndDownloadDependenciesForInit pnpm run dev",
|
||||
"dev:launch-bosszhipin-login-page-with-preload-extension-only": "cross-env MAIN_BOSSGEEKGO_UI_RUN_MODE=launchBossZhipinLoginPageWithPreloadExtension pnpm run dev",
|
||||
"build": "node scripts/run-build-sqlite-plugin.mjs && electron-vite build",
|
||||
"format": "prettier --write .",
|
||||
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts,.vue --fix",
|
||||
|
||||
41
packages/ui/src/main/features/run-common.ts
Normal file
41
packages/ui/src/main/features/run-common.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { AUTO_CHAT_ERROR_EXIT_CODE } from "../../common/enums/auto-start-chat"
|
||||
import { getAnyAvailablePuppeteerExecutable } from "../flow/CHECK_AND_DOWNLOAD_DEPENDENCIES/utils/puppeteer-executable"
|
||||
import { sendToDaemon } from "../flow/OPEN_SETTING_WINDOW/connect-to-daemon"
|
||||
import { saveAndGetCurrentRunRecord } from "../flow/OPEN_SETTING_WINDOW/utils/db"
|
||||
|
||||
export async function runCommon ({ mode }) {
|
||||
const puppeteerExecutable = await getAnyAvailablePuppeteerExecutable()
|
||||
if (!puppeteerExecutable) {
|
||||
return Promise.reject('NEED_TO_CHECK_RUNTIME_DEPENDENCIES')
|
||||
}
|
||||
const currentRunRecord = (await saveAndGetCurrentRunRecord())?.data
|
||||
const subProcessEnv = {
|
||||
...process.env,
|
||||
PUPPETEER_EXECUTABLE_PATH: puppeteerExecutable.executablePath,
|
||||
GEEKGEEKRUND_NO_AUTO_RESTART_EXIT_CODE: [
|
||||
AUTO_CHAT_ERROR_EXIT_CODE.PUPPETEER_IS_NOT_EXECUTABLE,
|
||||
AUTO_CHAT_ERROR_EXIT_CODE.LOGIN_STATUS_INVALID,
|
||||
AUTO_CHAT_ERROR_EXIT_CODE.LLM_UNAVAILABLE
|
||||
].join(',')
|
||||
}
|
||||
const args = process.env.NODE_ENV === 'development' ? [
|
||||
process.argv[1],
|
||||
`--mode=${mode}`,
|
||||
`--run-record-id=${currentRunRecord?.id || 0}`
|
||||
] : [
|
||||
`--mode=${mode}`,
|
||||
`--run-record-id=${currentRunRecord?.id || 0}`
|
||||
]
|
||||
await sendToDaemon(
|
||||
{
|
||||
type: 'start-worker',
|
||||
workerId: mode,
|
||||
command: process.argv[0],
|
||||
args,
|
||||
env: subProcessEnv
|
||||
},
|
||||
{
|
||||
needCallback: true
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
|
||||
import { EventEmitter } from "node:events";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import fs from "node:fs";
|
||||
|
||||
const net = require('net');
|
||||
const split2 = require('split2');
|
||||
@@ -20,6 +21,16 @@ export async function connectToDaemon() {
|
||||
const ipcSocketPath = process.platform === 'win32'
|
||||
? `\\\\.\\pipe\\${ipcSocketName}`
|
||||
: path.join(tmpdir(), `${ipcSocketName}.sock`)
|
||||
if (process.platform !== 'win32' ) {
|
||||
if (!fs.existsSync(ipcSocketPath)) {
|
||||
fs.writeFileSync(ipcSocketPath, '')
|
||||
}
|
||||
// 设置权限(Unix)
|
||||
fs.chmodSync(
|
||||
ipcSocketPath,
|
||||
0o777
|
||||
)
|
||||
}
|
||||
daemonClient.connect(ipcSocketPath, 'localhost', () => {
|
||||
isConnected = true
|
||||
console.log('已连接到守护进程');
|
||||
|
||||
@@ -53,6 +53,7 @@ import { RequestSceneEnum } from '../../../features/llm-request-log'
|
||||
import { checkUpdateForUi } from '../../../features/updater'
|
||||
import gtag from '../../../utils/gtag'
|
||||
import { daemonEE, sendToDaemon } from '../connect-to-daemon'
|
||||
import { runCommon } from '../../../features/run-common'
|
||||
|
||||
export default function initIpc() {
|
||||
ipcMain.handle('fetch-config-file-content', async () => {
|
||||
@@ -200,38 +201,10 @@ export default function initIpc() {
|
||||
})
|
||||
|
||||
ipcMain.handle('run-geek-auto-start-chat-with-boss', async (ev) => {
|
||||
const puppeteerExecutable = await getAnyAvailablePuppeteerExecutable()
|
||||
if (!puppeteerExecutable) {
|
||||
return Promise.reject('NEED_TO_CHECK_RUNTIME_DEPENDENCIES')
|
||||
}
|
||||
const currentRunRecord = (await saveAndGetCurrentRunRecord())?.data
|
||||
const subProcessEnv = {
|
||||
...process.env,
|
||||
PUPPETEER_EXECUTABLE_PATH: puppeteerExecutable.executablePath,
|
||||
GEEKGEEKRUND_NO_RESTART_EXIT_CODE: [
|
||||
AUTO_CHAT_ERROR_EXIT_CODE.PUPPETEER_IS_NOT_EXECUTABLE,
|
||||
AUTO_CHAT_ERROR_EXIT_CODE.LOGIN_STATUS_INVALID,
|
||||
AUTO_CHAT_ERROR_EXIT_CODE.LLM_UNAVAILABLE
|
||||
].join(',')
|
||||
}
|
||||
await sendToDaemon(
|
||||
{
|
||||
type: 'start-worker',
|
||||
workerId: 'geekAutoStartWithBossMain',
|
||||
command: process.argv[0],
|
||||
args: [
|
||||
process.argv[1],
|
||||
`--mode=geekAutoStartWithBossMain`,
|
||||
`--run-record-id=${currentRunRecord?.id || 0}`
|
||||
],
|
||||
env: subProcessEnv
|
||||
},
|
||||
{
|
||||
needCallback: true
|
||||
}
|
||||
)
|
||||
const mode = 'geekAutoStartWithBossMain'
|
||||
await runCommon({ mode })
|
||||
daemonEE.on('message', function handler (message) {
|
||||
if (message.workerId !== 'geekAutoStartWithBossMain') {
|
||||
if (message.workerId !== mode) {
|
||||
return
|
||||
}
|
||||
if (message.type === 'worker-exited') {
|
||||
@@ -256,38 +229,10 @@ export default function initIpc() {
|
||||
})
|
||||
|
||||
ipcMain.handle('run-read-no-reply-auto-reminder', async () => {
|
||||
const puppeteerExecutable = await getAnyAvailablePuppeteerExecutable()
|
||||
if (!puppeteerExecutable) {
|
||||
return Promise.reject('NEED_TO_CHECK_RUNTIME_DEPENDENCIES')
|
||||
}
|
||||
const currentRunRecord = (await saveAndGetCurrentRunRecord())?.data
|
||||
const subProcessEnv = {
|
||||
...process.env,
|
||||
PUPPETEER_EXECUTABLE_PATH: puppeteerExecutable.executablePath,
|
||||
GEEKGEEKRUND_NO_RESTART_EXIT_CODE: [
|
||||
AUTO_CHAT_ERROR_EXIT_CODE.PUPPETEER_IS_NOT_EXECUTABLE,
|
||||
AUTO_CHAT_ERROR_EXIT_CODE.LOGIN_STATUS_INVALID,
|
||||
AUTO_CHAT_ERROR_EXIT_CODE.LLM_UNAVAILABLE
|
||||
].join(',')
|
||||
}
|
||||
await sendToDaemon(
|
||||
{
|
||||
type: 'start-worker',
|
||||
workerId: 'readNoReplyAutoReminder',
|
||||
command: process.argv[0],
|
||||
args: [
|
||||
process.argv[1],
|
||||
`--mode=readNoReplyAutoReminder`,
|
||||
`--run-record-id=${currentRunRecord?.id || 0}`
|
||||
],
|
||||
env: subProcessEnv
|
||||
},
|
||||
{
|
||||
needCallback: true
|
||||
}
|
||||
)
|
||||
const mode = 'readNoReplyAutoReminderMain'
|
||||
await runCommon({ mode })
|
||||
daemonEE.on('message', function handler (message) {
|
||||
if (message.workerId !== 'readNoReplyAutoReminder') {
|
||||
if (message.workerId !== mode) {
|
||||
return
|
||||
}
|
||||
if (message.type === 'worker-exited') {
|
||||
@@ -400,7 +345,7 @@ export default function initIpc() {
|
||||
mainWindow?.webContents.send('read-no-reply-auto-reminder-stopping')
|
||||
const p = new Promise(resolve => {
|
||||
daemonEE.on('message', function handler (message) {
|
||||
if (message.workerId !== 'readNoReplyAutoReminder') {
|
||||
if (message.workerId !== 'readNoReplyAutoReminderMain') {
|
||||
return
|
||||
}
|
||||
if (message.type === 'worker-exited') {
|
||||
@@ -412,7 +357,7 @@ export default function initIpc() {
|
||||
await sendToDaemon(
|
||||
{
|
||||
type: 'stop-worker',
|
||||
workerId: 'readNoReplyAutoReminder',
|
||||
workerId: 'readNoReplyAutoReminderMain',
|
||||
},
|
||||
{
|
||||
needCallback: true
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { randomUUID } from "crypto";
|
||||
const { app } = require('electron');
|
||||
const { spawn } = require('child_process');
|
||||
|
||||
@@ -28,11 +27,6 @@ export function launchDaemon() {
|
||||
// 启动守护进程
|
||||
async function startDaemon() {
|
||||
console.log('启动守护进程...');
|
||||
process.env.GEEKGEEKRUND_PIPE_NAME = `geekgeekrun-d_${randomUUID()}`
|
||||
// 使用 Electron 可执行程序路径,如果没有则回退到 node
|
||||
const electronPath = process.execPath;
|
||||
console.log(`使用 Electron 路径: ${electronPath}`);
|
||||
|
||||
// 添加参数使守护进程在后台运行,不显示 UI
|
||||
daemonProcess = spawn(
|
||||
process.argv[0],
|
||||
@@ -48,19 +42,6 @@ export function launchDaemon() {
|
||||
}
|
||||
)
|
||||
|
||||
// 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}`);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import minimist from 'minimist'
|
||||
import { runCommon } from './features/run-common';
|
||||
import { launchDaemon } from './flow/OPEN_SETTING_WINDOW/launch-daemon';
|
||||
import { connectToDaemon } from './flow/OPEN_SETTING_WINDOW/connect-to-daemon';
|
||||
import { randomUUID } from 'crypto';
|
||||
const isUiDev = process.env.NODE_ENV === 'development'
|
||||
const commandlineArgs = minimist(isUiDev ? process.argv.slice(2) : process.argv.slice(1))
|
||||
console.log(commandlineArgs)
|
||||
@@ -7,6 +11,7 @@ const runMode = commandlineArgs['mode'];
|
||||
|
||||
;(async () => {
|
||||
switch (runMode) {
|
||||
// #region internal use
|
||||
case 'geekAutoStartWithBossMain': {
|
||||
const { waitForProcessHandShakeAndRunAutoChat } = await import(
|
||||
'./flow/GEEK_AUTO_START_CHAT_WITH_BOSS_MAIN/index'
|
||||
@@ -33,7 +38,7 @@ const runMode = commandlineArgs['mode'];
|
||||
launchBossSite()
|
||||
break
|
||||
}
|
||||
case 'readNoReplyAutoReminder': {
|
||||
case 'readNoReplyAutoReminderMain': {
|
||||
const { runEntry } = await import('./flow/READ_NO_REPLY_AUTO_REMINDER/index')
|
||||
runEntry()
|
||||
break
|
||||
@@ -42,10 +47,29 @@ const runMode = commandlineArgs['mode'];
|
||||
await import('./flow/LAUNCH_DAEMON')
|
||||
break
|
||||
}
|
||||
// #endregion
|
||||
|
||||
// #region user entry
|
||||
case 'geekAutoStartWithBoss': {
|
||||
process.env.GEEKGEEKRUND_PIPE_NAME = `geekgeekrun-d_${randomUUID()}`
|
||||
await launchDaemon()
|
||||
await connectToDaemon()
|
||||
await runCommon({ mode: 'geekAutoStartWithBossMain' })
|
||||
break
|
||||
}
|
||||
case 'readNoReplyAutoReminder': {
|
||||
process.env.GEEKGEEKRUND_PIPE_NAME = `geekgeekrun-d_${randomUUID()}`
|
||||
await launchDaemon()
|
||||
await connectToDaemon()
|
||||
await runCommon({ mode: 'readNoReplyAutoReminderMain' })
|
||||
break
|
||||
}
|
||||
default: {
|
||||
process.env.GEEKGEEKRUND_PIPE_NAME = `geekgeekrun-d_${randomUUID()}`
|
||||
const { openSettingWindow } = await import('./flow/OPEN_SETTING_WINDOW/index')
|
||||
openSettingWindow()
|
||||
break
|
||||
}
|
||||
// #region
|
||||
}
|
||||
})()
|
||||
|
||||
Reference in New Issue
Block a user