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