mirror of
https://github.com/geekgeekrun/geekgeekrun.git
synced 2026-09-08 00:47:15 +08:00
migrate daemon to new daemon
This commit is contained in:
@@ -1494,7 +1494,10 @@ export async function closeBrowserWindow () {
|
|||||||
browser?.close()
|
browser?.close()
|
||||||
const browserProcess = browser?.process()
|
const browserProcess = browser?.process()
|
||||||
if (browserProcess) {
|
if (browserProcess) {
|
||||||
process.kill(browserProcess.pid)
|
try {
|
||||||
|
process.kill(browserProcess.pid)
|
||||||
|
}
|
||||||
|
catch {}
|
||||||
}
|
}
|
||||||
browser = null
|
browser = null
|
||||||
page = null
|
page = null
|
||||||
|
|||||||
+21
-8
@@ -33,6 +33,7 @@ const PORT = 12345;
|
|||||||
const workers = new Map(); // workerId -> { process, status, restartCount, socket }
|
const workers = new Map(); // workerId -> { process, status, restartCount, socket }
|
||||||
const guiClients = new Set(); // GUI客户端连接集合
|
const guiClients = new Set(); // GUI客户端连接集合
|
||||||
const stoppedWorkers = new Set(); // 被用户主动停止的workerId集合,用于防止竞态条件
|
const stoppedWorkers = new Set(); // 被用户主动停止的workerId集合,用于防止竞态条件
|
||||||
|
const pidToProcessInfoMap = new Map()
|
||||||
|
|
||||||
// 创建TCP服务器
|
// 创建TCP服务器
|
||||||
const server = net.createServer((socket) => {
|
const server = net.createServer((socket) => {
|
||||||
@@ -137,7 +138,6 @@ function handleMessage(socket, message) {
|
|||||||
const shouldExit = stoppedWorkers.has(workerId) || !workers.has(workerId);
|
const shouldExit = stoppedWorkers.has(workerId) || !workers.has(workerId);
|
||||||
|
|
||||||
sendResponse(socket, _callbackUuid, {
|
sendResponse(socket, _callbackUuid, {
|
||||||
type: 'check-should-exit-response',
|
|
||||||
workerId: workerId,
|
workerId: workerId,
|
||||||
shouldExit: shouldExit
|
shouldExit: shouldExit
|
||||||
});
|
});
|
||||||
@@ -228,6 +228,11 @@ function startWorker({ workerId, command, args, env }, restartCount = 0) {
|
|||||||
console.log(`工具进程 ${workerId} 已在运行`);
|
console.log(`工具进程 ${workerId} 已在运行`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const noRestartExitCodeSet = new Set([0]);
|
||||||
|
(env.GEEKGEEKRUND_NO_RESTART_EXIT_CODE ?? '')
|
||||||
|
.split(',')
|
||||||
|
.map(n => parseInt(n))
|
||||||
|
.forEach(n => noRestartExitCodeSet.add(n))
|
||||||
|
|
||||||
console.log(`启动工具进程: ${workerId}${restartCount > 0 ? ` (重启第${restartCount}次)` : ''}`);
|
console.log(`启动工具进程: ${workerId}${restartCount > 0 ? ` (重启第${restartCount}次)` : ''}`);
|
||||||
// 添加参数使工具进程在后台运行,不显示 UI
|
// 添加参数使工具进程在后台运行,不显示 UI
|
||||||
@@ -253,15 +258,15 @@ function startWorker({ workerId, command, args, env }, restartCount = 0) {
|
|||||||
|
|
||||||
workerProcess.on('exit', (code, signal) => {
|
workerProcess.on('exit', (code, signal) => {
|
||||||
console.log(`工具进程 ${workerId} 退出,代码: ${code}, 信号: ${signal}`);
|
console.log(`工具进程 ${workerId} 退出,代码: ${code}, 信号: ${signal}`);
|
||||||
|
const workerInfo = pidToProcessInfoMap.get(workerProcess.pid)
|
||||||
const workerInfo = workers.get(workerId);
|
|
||||||
if (workerInfo) {
|
if (workerInfo) {
|
||||||
|
pidToProcessInfoMap.delete(workerProcess.pid)
|
||||||
// 关闭工具进程的TCP连接
|
// 关闭工具进程的TCP连接
|
||||||
if (workerInfo.socket) {
|
if (workerInfo.socket) {
|
||||||
workerInfo.socket.destroy();
|
workerInfo.socket.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
const shouldRestart = code !== 0 // && code !== null;
|
const shouldRestart = !noRestartExitCodeSet.has(code) // && code !== null;
|
||||||
// 使用当前的 restartCount 加1,而不是从 workerInfo 中取(因为可能已经被删除)
|
// 使用当前的 restartCount 加1,而不是从 workerInfo 中取(因为可能已经被删除)
|
||||||
const restartCount = (workerInfo.restartCount || 0) + 1;
|
const restartCount = (workerInfo.restartCount || 0) + 1;
|
||||||
|
|
||||||
@@ -290,6 +295,14 @@ function startWorker({ workerId, command, args, env }, restartCount = 0) {
|
|||||||
console.log(`工具进程 ${workerId} 在重启前被标记为停止,取消重启`);
|
console.log(`工具进程 ${workerId} 在重启前被标记为停止,取消重启`);
|
||||||
// 从停止列表中移除,因为已经处理完毕
|
// 从停止列表中移除,因为已经处理完毕
|
||||||
stoppedWorkers.delete(workerId);
|
stoppedWorkers.delete(workerId);
|
||||||
|
broadcastToGUI({
|
||||||
|
type: 'worker-exited',
|
||||||
|
workerId: workerId,
|
||||||
|
code: code,
|
||||||
|
signal: signal,
|
||||||
|
restarting: false,
|
||||||
|
restartCount: restartCount
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, 2000);
|
}, 2000);
|
||||||
} else if (stoppedWorkers.has(workerId)) {
|
} else if (stoppedWorkers.has(workerId)) {
|
||||||
@@ -310,7 +323,7 @@ function startWorker({ workerId, command, args, env }, restartCount = 0) {
|
|||||||
console.log(err)
|
console.log(err)
|
||||||
})
|
})
|
||||||
|
|
||||||
workers.set(workerId, {
|
const workerInfo = {
|
||||||
process: workerProcess,
|
process: workerProcess,
|
||||||
status: 'running',
|
status: 'running',
|
||||||
startTime: Date.now(),
|
startTime: Date.now(),
|
||||||
@@ -320,8 +333,9 @@ function startWorker({ workerId, command, args, env }, restartCount = 0) {
|
|||||||
command,
|
command,
|
||||||
env,
|
env,
|
||||||
workerId,
|
workerId,
|
||||||
});
|
}
|
||||||
|
workers.set(workerId, workerInfo);
|
||||||
|
pidToProcessInfoMap.set(workerProcess.pid, workerInfo);
|
||||||
// 定期发送状态更新
|
// 定期发送状态更新
|
||||||
broadcastStatus();
|
broadcastStatus();
|
||||||
}
|
}
|
||||||
@@ -333,7 +347,6 @@ function stopWorker(workerId) {
|
|||||||
// 无论workerInfo是否存在,都添加到停止列表,防止竞态条件
|
// 无论workerInfo是否存在,都添加到停止列表,防止竞态条件
|
||||||
stoppedWorkers.add(workerId);
|
stoppedWorkers.add(workerId);
|
||||||
console.log(`停止工具进程: ${workerId} (已添加到停止列表)`);
|
console.log(`停止工具进程: ${workerId} (已添加到停止列表)`);
|
||||||
|
|
||||||
if (!workerInfo) {
|
if (!workerInfo) {
|
||||||
console.log(`工具进程 ${workerId} 不存在,但已标记为停止(防止重启)`);
|
console.log(`工具进程 ${workerId} 不存在,但已标记为停止(防止重启)`);
|
||||||
// 通知GUI客户端
|
// 通知GUI客户端
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ function sendToDaemon(message) {
|
|||||||
function handleDaemonMessage(message) {
|
function handleDaemonMessage(message) {
|
||||||
if (message.type === 'worker-registered') {
|
if (message.type === 'worker-registered') {
|
||||||
console.log(`[工具进程 ${workerId}] 连接已注册到守护进程`);
|
console.log(`[工具进程 ${workerId}] 连接已注册到守护进程`);
|
||||||
} else if (message.type === 'check-should-exit-response') {
|
} else if (message.type === 'check-should-exit') {
|
||||||
// 处理是否应该退出的查询响应
|
// 处理是否应该退出的查询响应
|
||||||
if (message.shouldExit) {
|
if (message.shouldExit) {
|
||||||
console.log(`[工具进程 ${workerId}] 守护进程指示应该退出,正在退出...`);
|
console.log(`[工具进程 ${workerId}] 守护进程指示应该退出,正在退出...`);
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ export enum AUTO_CHAT_ERROR_EXIT_CODE {
|
|||||||
ERR_INTERNET_DISCONNECTED = 83,
|
ERR_INTERNET_DISCONNECTED = 83,
|
||||||
ACCESS_IS_DENIED = 84,
|
ACCESS_IS_DENIED = 84,
|
||||||
PUPPETEER_IS_NOT_EXECUTABLE = 85,
|
PUPPETEER_IS_NOT_EXECUTABLE = 85,
|
||||||
AUTO_START_CHAT_DAEMON_PROCESS_SUICIDE = 86,
|
LLM_UNAVAILABLE = 86,
|
||||||
AUTO_START_CHAT_MAIN_PROCESS_SUICIDE = 87,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum RECHAT_CONTENT_SOURCE {
|
export enum RECHAT_CONTENT_SOURCE {
|
||||||
|
|||||||
@@ -1,129 +0,0 @@
|
|||||||
import { sleep } from '@geekgeekrun/utils/sleep.mjs'
|
|
||||||
import childProcess from 'node:child_process'
|
|
||||||
import { AUTO_CHAT_ERROR_EXIT_CODE } from '../../../common/enums/auto-start-chat'
|
|
||||||
import { app, dialog } from 'electron'
|
|
||||||
import fs, { WriteStream } from 'node:fs'
|
|
||||||
import { pipeWriteRegardlessError } from '../utils/pipe'
|
|
||||||
import * as JSONStream from 'JSONStream'
|
|
||||||
import { initPowerSaveBlocker } from './power-saver-blocker'
|
|
||||||
import gtag from '../../utils/gtag'
|
|
||||||
import { initDb } from '@geekgeekrun/sqlite-plugin'
|
|
||||||
import { getPublicDbFilePath } from '@geekgeekrun/geek-auto-start-chat-with-boss/runtime-file-utils.mjs'
|
|
||||||
import { AutoStartChatRunRecord } from '@geekgeekrun/sqlite-plugin/dist/entity/AutoStartChatRunRecord'
|
|
||||||
import minimist from 'minimist'
|
|
||||||
import attachListenerForKillSelfOnParentExited from '../../utils/attachListenerForKillSelfOnParentExited'
|
|
||||||
const isUiDev = process.env.NODE_ENV === 'development'
|
|
||||||
const rerunInterval = (() => {
|
|
||||||
let v = Number(process.env.MAIN_BOSSGEEKGO_RERUN_INTERVAL)
|
|
||||||
if (isNaN(v)) {
|
|
||||||
v = 3000
|
|
||||||
}
|
|
||||||
|
|
||||||
return v
|
|
||||||
})()
|
|
||||||
function runWithDaemon({ runRecordId, runMode, parentProcessPipe }) {
|
|
||||||
const subProcessOfCore = childProcess.spawn(
|
|
||||||
process.argv[0],
|
|
||||||
isUiDev
|
|
||||||
? [process.argv[1], `--run-record-id=${runRecordId}`, `--mode=${runMode}`]
|
|
||||||
: [`--run-record-id=${runRecordId}`, `--mode=${runMode}`],
|
|
||||||
{
|
|
||||||
stdio: ['inherit', 'inherit', 'inherit', 'pipe', 'ipc']
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
subProcessOfCore!.stdio[3]!.pipe(JSONStream.parse()).on('data', async (raw) => {
|
|
||||||
const data = raw
|
|
||||||
switch (data.type) {
|
|
||||||
case 'GEEK_AUTO_START_CHAT_WITH_BOSS_STARTED': {
|
|
||||||
pipeWriteRegardlessError(
|
|
||||||
parentProcessPipe as WriteStream,
|
|
||||||
JSON.stringify({
|
|
||||||
type: data.type
|
|
||||||
})
|
|
||||||
)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
subProcessOfCore.once('exit', async (exitCode: number) => {
|
|
||||||
if (
|
|
||||||
[...Object.values(AUTO_CHAT_ERROR_EXIT_CODE)]
|
|
||||||
.filter((it) => typeof it === 'number')
|
|
||||||
.includes(exitCode)
|
|
||||||
) {
|
|
||||||
console.log(
|
|
||||||
`[Run core daemon] Child process exit with reason ${AUTO_CHAT_ERROR_EXIT_CODE[exitCode]}.`
|
|
||||||
)
|
|
||||||
process.exit(exitCode)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
console.log(
|
|
||||||
`[Run core daemon] Child process exit with code ${exitCode}, an internal error may not be caught, and will be restarted in ${rerunInterval}ms.`
|
|
||||||
)
|
|
||||||
await sleep(rerunInterval)
|
|
||||||
runWithDaemon({ runRecordId, runMode, parentProcessPipe })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function runAutoChatWithDaemon() {
|
|
||||||
const commandlineArgs = minimist(isUiDev ? process.argv.slice(2) : process.argv.slice(1))
|
|
||||||
if (!['geekAutoStartWithBossMain'].includes(commandlineArgs['mode-to-daemon'])) {
|
|
||||||
await new Promise((resolve) => {
|
|
||||||
app.once('ready', () => resolve(undefined))
|
|
||||||
})
|
|
||||||
|
|
||||||
dialog.showMessageBoxSync({
|
|
||||||
type: 'error',
|
|
||||||
message: `守护进程不支持 ${commandlineArgs['mode-to-daemon'] ?? '(默认)'} 模式`
|
|
||||||
})
|
|
||||||
app.exit()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
app.dock?.hide()
|
|
||||||
process.on('disconnect', () => {
|
|
||||||
app.exit()
|
|
||||||
})
|
|
||||||
|
|
||||||
let pipe: null | fs.WriteStream = null
|
|
||||||
try {
|
|
||||||
pipe = fs.createWriteStream(null, { fd: 3 })
|
|
||||||
} catch {
|
|
||||||
console.error('pipe is not available')
|
|
||||||
app.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
const disposePowerSaveBlocker = initPowerSaveBlocker()
|
|
||||||
app.once('quit', disposePowerSaveBlocker)
|
|
||||||
|
|
||||||
process.on('SIGINT', () => {
|
|
||||||
process.exit()
|
|
||||||
})
|
|
||||||
|
|
||||||
const ds = await initDb(getPublicDbFilePath())
|
|
||||||
const autoStartChatRunRecord = new AutoStartChatRunRecord()
|
|
||||||
autoStartChatRunRecord.date = new Date()
|
|
||||||
const autoStartChatRunRecordRepository = ds.getRepository(AutoStartChatRunRecord)
|
|
||||||
const result = await autoStartChatRunRecordRepository.save(autoStartChatRunRecord)
|
|
||||||
runWithDaemon({
|
|
||||||
runRecordId: result.id,
|
|
||||||
runMode: commandlineArgs['mode-to-daemon'],
|
|
||||||
parentProcessPipe: pipe
|
|
||||||
})
|
|
||||||
|
|
||||||
pipeWriteRegardlessError(
|
|
||||||
pipe,
|
|
||||||
JSON.stringify({
|
|
||||||
type: 'AUTO_START_CHAT_DAEMON_PROCESS_STARTUP'
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
gtag('daemon_ready', { mode: commandlineArgs['mode-to-daemon'] ?? '' })
|
|
||||||
}
|
|
||||||
|
|
||||||
attachListenerForKillSelfOnParentExited()
|
|
||||||
-10
@@ -1,10 +0,0 @@
|
|||||||
import { powerSaveBlocker } from 'electron'
|
|
||||||
|
|
||||||
export const initPowerSaveBlocker = (
|
|
||||||
type: 'prevent-app-suspension' | 'prevent-display-sleep' = 'prevent-app-suspension'
|
|
||||||
) => {
|
|
||||||
const id = powerSaveBlocker.start(type)
|
|
||||||
return function disposePowerSaveBlocker() {
|
|
||||||
return powerSaveBlocker.stop(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -16,7 +16,7 @@ import attachListenerForKillSelfOnParentExited from '../../utils/attachListenerF
|
|||||||
import SqlitePluginModule from '@geekgeekrun/sqlite-plugin'
|
import SqlitePluginModule from '@geekgeekrun/sqlite-plugin'
|
||||||
import gtag from '../../utils/gtag'
|
import gtag from '../../utils/gtag'
|
||||||
import GtagPlugin from '../../utils/gtag/GtagPlugin'
|
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'
|
||||||
const { default: SqlitePlugin } = SqlitePluginModule
|
const { default: SqlitePlugin } = SqlitePluginModule
|
||||||
|
|
||||||
const rerunInterval = (() => {
|
const rerunInterval = (() => {
|
||||||
@@ -36,10 +36,18 @@ const initPlugins = (hooks) => {
|
|||||||
new GtagPlugin().apply(hooks)
|
new GtagPlugin().apply(hooks)
|
||||||
}
|
}
|
||||||
|
|
||||||
let isParentProcessDisconnect = false
|
async function checkShouldExit () {
|
||||||
process.once('disconnect', () => {
|
const shouldExitResponse = await sendToDaemon(
|
||||||
isParentProcessDisconnect = true
|
{
|
||||||
})
|
type: 'check-should-exit',
|
||||||
|
workerId: process.env.GEEKGEEKRUND_WORKER_ID,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
needCallback: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return shouldExitResponse?.shouldExit
|
||||||
|
}
|
||||||
|
|
||||||
const runAutoChat = async () => {
|
const runAutoChat = async () => {
|
||||||
const { initPuppeteer, mainLoop, closeBrowserWindow, autoStartChatEventBus } = await import(
|
const { initPuppeteer, mainLoop, closeBrowserWindow, autoStartChatEventBus } = await import(
|
||||||
@@ -118,7 +126,7 @@ const runAutoChat = async () => {
|
|||||||
// )
|
// )
|
||||||
})
|
})
|
||||||
|
|
||||||
while (![isParentProcessDisconnect].includes(true)) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
await mainLoop(hooks)
|
await mainLoop(hooks)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -140,9 +148,18 @@ const runAutoChat = async () => {
|
|||||||
process.exit(AUTO_CHAT_ERROR_EXIT_CODE.ACCESS_IS_DENIED)
|
process.exit(AUTO_CHAT_ERROR_EXIT_CODE.ACCESS_IS_DENIED)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
if (err.message.includes(`Could not find Chrome`) || err.message.includes(`no executable was found`)) {
|
||||||
|
process.exit(AUTO_CHAT_ERROR_EXIT_CODE.PUPPETEER_IS_NOT_EXECUTABLE)
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
closeBrowserWindow?.()
|
closeBrowserWindow?.()
|
||||||
console.error(err)
|
console.error(err)
|
||||||
|
const shouldExit = await checkShouldExit()
|
||||||
|
if (shouldExit) {
|
||||||
|
app.exit()
|
||||||
|
return
|
||||||
|
}
|
||||||
console.log(
|
console.log(
|
||||||
`[Run core main] An internal error is caught, and browser will be restarted in ${rerunInterval}ms.`
|
`[Run core main] An internal error is caught, and browser will be restarted in ${rerunInterval}ms.`
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import { randomUUID } from "node:crypto";
|
import { randomUUID } from "node:crypto";
|
||||||
import { DAEMON_PORT } from "./daemon-config";
|
import { DAEMON_PORT } from "./daemon-config";
|
||||||
|
import { EventEmitter } from "node:events";
|
||||||
|
|
||||||
const net = require('net');
|
const net = require('net');
|
||||||
const split2 = require('split2');
|
const split2 = require('split2');
|
||||||
const { app } = require('electron');
|
const { app } = require('electron');
|
||||||
|
|
||||||
let daemonClient = null;
|
let daemonClient = null;
|
||||||
|
export const daemonEE = new EventEmitter()
|
||||||
const waitForCallbackTaskMap = new Map()
|
const waitForCallbackTaskMap = new Map()
|
||||||
|
|
||||||
// 连接到守护进程
|
// 连接到守护进程
|
||||||
@@ -13,6 +15,7 @@ export function connectToDaemon() {
|
|||||||
daemonClient = new net.Socket();
|
daemonClient = new net.Socket();
|
||||||
daemonClient.connect(DAEMON_PORT, 'localhost', () => {
|
daemonClient.connect(DAEMON_PORT, 'localhost', () => {
|
||||||
console.log('已连接到守护进程');
|
console.log('已连接到守护进程');
|
||||||
|
daemonEE.emit('connect')
|
||||||
// 通知渲染进程连接成功
|
// 通知渲染进程连接成功
|
||||||
// if (mainWindow) {
|
// if (mainWindow) {
|
||||||
// mainWindow.webContents.send('daemon-connected');
|
// mainWindow.webContents.send('daemon-connected');
|
||||||
@@ -27,6 +30,8 @@ export function connectToDaemon() {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const message = JSON.parse(trimmedLine);
|
const message = JSON.parse(trimmedLine);
|
||||||
|
daemonEE.emit('message', message)
|
||||||
|
// FIXME:
|
||||||
console.log('收到守护进程消息:', message);
|
console.log('收到守护进程消息:', message);
|
||||||
if (
|
if (
|
||||||
message._callbackUuid
|
message._callbackUuid
|
||||||
@@ -59,6 +64,7 @@ export function connectToDaemon() {
|
|||||||
|
|
||||||
daemonClient.on('error', (err) => {
|
daemonClient.on('error', (err) => {
|
||||||
console.error('守护进程连接错误:', err);
|
console.error('守护进程连接错误:', err);
|
||||||
|
daemonEE.emit('error', err)
|
||||||
// 尝试重连
|
// 尝试重连
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (daemonClient.destroyed) {
|
if (daemonClient.destroyed) {
|
||||||
@@ -69,6 +75,7 @@ export function connectToDaemon() {
|
|||||||
|
|
||||||
daemonClient.on('close', () => {
|
daemonClient.on('close', () => {
|
||||||
console.log('守护进程连接已关闭');
|
console.log('守护进程连接已关闭');
|
||||||
|
daemonEE.emit('close')
|
||||||
// 尝试重连
|
// 尝试重连
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
connectToDaemon();
|
connectToDaemon();
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import { ChildProcess } from 'child_process'
|
|||||||
import * as JSONStream from 'JSONStream'
|
import * as JSONStream from 'JSONStream'
|
||||||
import { checkCookieListFormat } from '../../../../common/utils/cookie'
|
import { checkCookieListFormat } from '../../../../common/utils/cookie'
|
||||||
import { getAnyAvailablePuppeteerExecutable } from '../../../flow/CHECK_AND_DOWNLOAD_DEPENDENCIES/utils/puppeteer-executable/index'
|
import { getAnyAvailablePuppeteerExecutable } from '../../../flow/CHECK_AND_DOWNLOAD_DEPENDENCIES/utils/puppeteer-executable/index'
|
||||||
import { sleep } from '@geekgeekrun/utils/sleep.mjs'
|
|
||||||
import { AUTO_CHAT_ERROR_EXIT_CODE } from '../../../../common/enums/auto-start-chat'
|
import { AUTO_CHAT_ERROR_EXIT_CODE } from '../../../../common/enums/auto-start-chat'
|
||||||
import { mainWindow } from '../../../window/mainWindow'
|
import { mainWindow } from '../../../window/mainWindow'
|
||||||
import {
|
import {
|
||||||
@@ -52,7 +51,7 @@ import {
|
|||||||
import { RequestSceneEnum } from '../../../features/llm-request-log'
|
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 { sendToDaemon } from '../connect-to-daemon'
|
import { daemonEE, sendToDaemon } from '../connect-to-daemon'
|
||||||
|
|
||||||
export default function initIpc() {
|
export default function initIpc() {
|
||||||
ipcMain.handle('fetch-config-file-content', async () => {
|
ipcMain.handle('fetch-config-file-content', async () => {
|
||||||
@@ -199,36 +198,20 @@ export default function initIpc() {
|
|||||||
return await writeStorageFile(payload.fileName, JSON.parse(payload.data))
|
return await writeStorageFile(payload.fileName, JSON.parse(payload.data))
|
||||||
})
|
})
|
||||||
|
|
||||||
// const currentExecutablePath = app.getPath('exe')
|
|
||||||
// console.log(currentExecutablePath)
|
|
||||||
|
|
||||||
let subProcessOfPuppeteer: ChildProcess | null = null
|
|
||||||
ipcMain.handle('run-geek-auto-start-chat-with-boss', async (ev) => {
|
ipcMain.handle('run-geek-auto-start-chat-with-boss', async (ev) => {
|
||||||
if (subProcessOfPuppeteer) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const puppeteerExecutable = await getAnyAvailablePuppeteerExecutable()
|
const puppeteerExecutable = await getAnyAvailablePuppeteerExecutable()
|
||||||
if (!puppeteerExecutable) {
|
if (!puppeteerExecutable) {
|
||||||
return Promise.reject('NEED_TO_CHECK_RUNTIME_DEPENDENCIES')
|
return Promise.reject('NEED_TO_CHECK_RUNTIME_DEPENDENCIES')
|
||||||
}
|
}
|
||||||
const subProcessEnv = {
|
const subProcessEnv = {
|
||||||
...process.env,
|
...process.env,
|
||||||
PUPPETEER_EXECUTABLE_PATH: puppeteerExecutable.executablePath
|
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(',')
|
||||||
}
|
}
|
||||||
// ipcMain.emit(
|
|
||||||
// 'start-worker',
|
|
||||||
// ev,
|
|
||||||
// {
|
|
||||||
// workerId: 'geekAutoStartWithBossMain',
|
|
||||||
// command: process.argv[0],
|
|
||||||
// args: [
|
|
||||||
// process.argv[1],
|
|
||||||
// `--mode=geekAutoStartWithBossMain`
|
|
||||||
// ],
|
|
||||||
// env: subProcessEnv
|
|
||||||
// }
|
|
||||||
// )
|
|
||||||
|
|
||||||
await sendToDaemon(
|
await sendToDaemon(
|
||||||
{
|
{
|
||||||
type: 'start-worker',
|
type: 'start-worker',
|
||||||
@@ -244,108 +227,83 @@ export default function initIpc() {
|
|||||||
needCallback: true
|
needCallback: true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
daemonEE.on('message', function handler (message) {
|
||||||
// subProcessOfPuppeteer = childProcess.spawn(
|
if (message.workerId !== 'geekAutoStartWithBossMain') {
|
||||||
// process.argv[0],
|
return
|
||||||
// [
|
}
|
||||||
// process.argv[1],
|
if (message.type === 'worker-exited') {
|
||||||
// `--mode=geekAutoStartWithBossDaemon`,
|
switch(message.code) {
|
||||||
// `--mode-to-daemon=geekAutoStartWithBossMain`
|
case AUTO_CHAT_ERROR_EXIT_CODE.PUPPETEER_IS_NOT_EXECUTABLE: {
|
||||||
// ],
|
mainWindow?.webContents.send('need-to-check-runtime-dependencies')
|
||||||
// {
|
daemonEE.off('message', handler)
|
||||||
// env: subProcessEnv,
|
break
|
||||||
// stdio: ['inherit', 'inherit', 'inherit', 'pipe', 'ipc']
|
}
|
||||||
// }
|
case AUTO_CHAT_ERROR_EXIT_CODE.LOGIN_STATUS_INVALID: {
|
||||||
// )
|
mainWindow?.webContents.send('check-boss-zhipin-cookie-file')
|
||||||
// // console.log(subProcessOfPuppeteer)
|
daemonEE.off('message', handler)
|
||||||
// return new Promise((resolve, reject) => {
|
break
|
||||||
// subProcessOfPuppeteer!.stdio[3]!.pipe(JSONStream.parse()).on('data', async (raw) => {
|
}
|
||||||
// const data = raw
|
case AUTO_CHAT_ERROR_EXIT_CODE.NORMAL: {
|
||||||
// switch (data.type) {
|
daemonEE.off('message', handler)
|
||||||
// case 'GEEK_AUTO_START_CHAT_WITH_BOSS_STARTED': {
|
break
|
||||||
// resolve(data)
|
}
|
||||||
// break
|
}
|
||||||
// }
|
}
|
||||||
// case 'LOGIN_STATUS_INVALID': {
|
})
|
||||||
// await sleep(500)
|
|
||||||
// mainWindow?.webContents.send('check-boss-zhipin-cookie-file')
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// default: {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
|
|
||||||
// subProcessOfPuppeteer!.once('exit', (exitCode) => {
|
|
||||||
// subProcessOfPuppeteer = null
|
|
||||||
// if (exitCode === AUTO_CHAT_ERROR_EXIT_CODE.PUPPETEER_IS_NOT_EXECUTABLE) {
|
|
||||||
// // means cannot find downloaded puppeteer
|
|
||||||
// reject('NEED_TO_CHECK_RUNTIME_DEPENDENCIES')
|
|
||||||
// } else {
|
|
||||||
// mainWindow?.webContents.send('geek-auto-start-chat-with-boss-stopped')
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// })
|
|
||||||
// // TODO:
|
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.handle('run-read-no-reply-auto-reminder', async () => {
|
ipcMain.handle('run-read-no-reply-auto-reminder', async () => {
|
||||||
if (subProcessOfPuppeteer) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const puppeteerExecutable = await getAnyAvailablePuppeteerExecutable()
|
const puppeteerExecutable = await getAnyAvailablePuppeteerExecutable()
|
||||||
if (!puppeteerExecutable) {
|
if (!puppeteerExecutable) {
|
||||||
return Promise.reject('NEED_TO_CHECK_RUNTIME_DEPENDENCIES')
|
return Promise.reject('NEED_TO_CHECK_RUNTIME_DEPENDENCIES')
|
||||||
}
|
}
|
||||||
const subProcessEnv = {
|
const subProcessEnv = {
|
||||||
...process.env,
|
...process.env,
|
||||||
PUPPETEER_EXECUTABLE_PATH: puppeteerExecutable.executablePath
|
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(',')
|
||||||
}
|
}
|
||||||
subProcessOfPuppeteer = childProcess.spawn(
|
await sendToDaemon(
|
||||||
process.argv[0],
|
|
||||||
[process.argv[1], `--mode=readNoReplyAutoReminder`],
|
|
||||||
{
|
{
|
||||||
env: subProcessEnv,
|
type: 'start-worker',
|
||||||
stdio: ['inherit', 'inherit', 'inherit', 'pipe', 'ipc']
|
workerId: 'readNoReplyAutoReminder',
|
||||||
|
command: process.argv[0],
|
||||||
|
args: [
|
||||||
|
process.argv[1],
|
||||||
|
`--mode=readNoReplyAutoReminder`
|
||||||
|
],
|
||||||
|
env: subProcessEnv
|
||||||
|
},
|
||||||
|
{
|
||||||
|
needCallback: true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
// console.log(subProcessOfPuppeteer)
|
daemonEE.on('message', function handler (message) {
|
||||||
return new Promise((resolve, reject) => {
|
if (message.workerId !== 'readNoReplyAutoReminder') {
|
||||||
subProcessOfPuppeteer!.stdio[3]!.pipe(JSONStream.parse()).on('data', async (raw) => {
|
return
|
||||||
const data = raw
|
}
|
||||||
switch (data.type) {
|
if (message.type === 'worker-exited') {
|
||||||
case 'LOGIN_STATUS_INVALID': {
|
switch(message.code) {
|
||||||
await sleep(500)
|
case AUTO_CHAT_ERROR_EXIT_CODE.PUPPETEER_IS_NOT_EXECUTABLE: {
|
||||||
|
mainWindow?.webContents.send('need-to-check-runtime-dependencies')
|
||||||
|
daemonEE.off('message', handler)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case AUTO_CHAT_ERROR_EXIT_CODE.LOGIN_STATUS_INVALID: {
|
||||||
mainWindow?.webContents.send('check-boss-zhipin-cookie-file')
|
mainWindow?.webContents.send('check-boss-zhipin-cookie-file')
|
||||||
return
|
daemonEE.off('message', handler)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
case 'ERR_INTERNET_DISCONNECTED': {
|
case AUTO_CHAT_ERROR_EXIT_CODE.NORMAL: {
|
||||||
mainWindow?.webContents.send('toast-message', {
|
daemonEE.off('message', handler)
|
||||||
type: 'error',
|
break
|
||||||
message: '联网失败,请检查网络连接'
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
subProcessOfPuppeteer!.once('exit', (exitCode) => {
|
|
||||||
subProcessOfPuppeteer = null
|
|
||||||
if (exitCode === AUTO_CHAT_ERROR_EXIT_CODE.PUPPETEER_IS_NOT_EXECUTABLE) {
|
|
||||||
// means cannot find downloaded puppeteer
|
|
||||||
reject('NEED_TO_CHECK_RUNTIME_DEPENDENCIES')
|
|
||||||
} else {
|
|
||||||
mainWindow?.webContents.send('geek-auto-start-chat-with-boss-stopped')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
resolve(true)
|
|
||||||
})
|
})
|
||||||
// TODO:
|
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.handle('check-dependencies', async () => {
|
ipcMain.handle('check-dependencies', async () => {
|
||||||
@@ -408,14 +366,56 @@ export default function initIpc() {
|
|||||||
|
|
||||||
ipcMain.handle('stop-geek-auto-start-chat-with-boss', async () => {
|
ipcMain.handle('stop-geek-auto-start-chat-with-boss', async () => {
|
||||||
mainWindow?.webContents.send('geek-auto-start-chat-with-boss-stopping')
|
mainWindow?.webContents.send('geek-auto-start-chat-with-boss-stopping')
|
||||||
subProcessOfPuppeteer?.kill()
|
const p = new Promise(resolve => {
|
||||||
setTimeout(() => {
|
daemonEE.on('message', function handler (message) {
|
||||||
try {
|
if (message.workerId !== 'geekAutoStartWithBossMain') {
|
||||||
subProcessOfPuppeteer?.kill('SIGKILL')
|
return
|
||||||
} catch {
|
}
|
||||||
//
|
if (message.type === 'worker-exited') {
|
||||||
|
daemonEE.off('message', handler)
|
||||||
|
resolve(undefined)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
await sendToDaemon(
|
||||||
|
{
|
||||||
|
type: 'stop-worker',
|
||||||
|
workerId: 'geekAutoStartWithBossMain',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
needCallback: true
|
||||||
}
|
}
|
||||||
}, 1000)
|
)
|
||||||
|
|
||||||
|
await p
|
||||||
|
mainWindow?.webContents.send('geek-auto-start-chat-with-boss-stopped')
|
||||||
|
})
|
||||||
|
|
||||||
|
ipcMain.handle('stop-read-no-reply-auto-reminder', async () => {
|
||||||
|
mainWindow?.webContents.send('read-no-reply-auto-reminder-stopping')
|
||||||
|
const p = new Promise(resolve => {
|
||||||
|
daemonEE.on('message', function handler (message) {
|
||||||
|
if (message.workerId !== 'readNoReplyAutoReminder') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (message.type === 'worker-exited') {
|
||||||
|
daemonEE.off('message', handler)
|
||||||
|
resolve(undefined)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
await sendToDaemon(
|
||||||
|
{
|
||||||
|
type: 'stop-worker',
|
||||||
|
workerId: 'readNoReplyAutoReminder',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
needCallback: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
await p
|
||||||
|
mainWindow?.webContents.send('read-no-reply-auto-reminder-stopped')
|
||||||
})
|
})
|
||||||
|
|
||||||
let subProcessOfBossZhipinLoginPageWithPreloadExtension: ChildProcess | null = null
|
let subProcessOfBossZhipinLoginPageWithPreloadExtension: ChildProcess | null = null
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { Browser, Page } from 'puppeteer'
|
|||||||
import { sendGptContent, sendLookForwardReplyEmotion } from './boss-operation'
|
import { sendGptContent, sendLookForwardReplyEmotion } from './boss-operation'
|
||||||
import { sleep, sleepWithRandomDelay } from '@geekgeekrun/utils/sleep.mjs'
|
import { sleep, sleepWithRandomDelay } from '@geekgeekrun/utils/sleep.mjs'
|
||||||
import { waitForPage } from '@geekgeekrun/utils/puppeteer/wait.mjs'
|
import { waitForPage } from '@geekgeekrun/utils/puppeteer/wait.mjs'
|
||||||
import attachListenerForKillSelfOnParentExited from '../../utils/attachListenerForKillSelfOnParentExited'
|
|
||||||
import { app, dialog } from 'electron'
|
import { app, dialog } from 'electron'
|
||||||
import { initDb } from '@geekgeekrun/sqlite-plugin'
|
import { initDb } from '@geekgeekrun/sqlite-plugin'
|
||||||
import {
|
import {
|
||||||
@@ -18,15 +17,14 @@ import {
|
|||||||
saveJobHireStatusRecord
|
saveJobHireStatusRecord
|
||||||
} from '@geekgeekrun/sqlite-plugin/dist/handlers'
|
} from '@geekgeekrun/sqlite-plugin/dist/handlers'
|
||||||
import { writeStorageFile } from '@geekgeekrun/geek-auto-start-chat-with-boss/runtime-file-utils.mjs'
|
import { writeStorageFile } from '@geekgeekrun/geek-auto-start-chat-with-boss/runtime-file-utils.mjs'
|
||||||
import * as fs from 'fs'
|
|
||||||
import { pipeWriteRegardlessError } from '../utils/pipe'
|
|
||||||
import { BossInfo } from '@geekgeekrun/sqlite-plugin/dist/entity/BossInfo'
|
import { BossInfo } from '@geekgeekrun/sqlite-plugin/dist/entity/BossInfo'
|
||||||
import { messageForSaveFilter } from '../../../common/utils/chat-list'
|
import { messageForSaveFilter } from '../../../common/utils/chat-list'
|
||||||
import { RECHAT_CONTENT_SOURCE, RECHAT_LLM_FALLBACK } from '../../../common/enums/auto-start-chat'
|
import { AUTO_CHAT_ERROR_EXIT_CODE, RECHAT_CONTENT_SOURCE, RECHAT_LLM_FALLBACK } from '../../../common/enums/auto-start-chat'
|
||||||
import gtag from '../../utils/gtag'
|
import gtag from '../../utils/gtag'
|
||||||
import { JobHireStatus } from '@geekgeekrun/sqlite-plugin/dist/enums';
|
import { JobHireStatus } from '@geekgeekrun/sqlite-plugin/dist/enums';
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import cheerio from 'cheerio'
|
import cheerio from 'cheerio'
|
||||||
|
import { connectToDaemon, sendToDaemon } from '../OPEN_SETTING_WINDOW/connect-to-daemon'
|
||||||
|
|
||||||
const throttleIntervalMinutes =
|
const throttleIntervalMinutes =
|
||||||
readConfigFile('boss.json').autoReminder?.throttleIntervalMinutes ?? 10
|
readConfigFile('boss.json').autoReminder?.throttleIntervalMinutes ?? 10
|
||||||
@@ -444,10 +442,6 @@ const mainLoop = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let isParentProcessDisconnect = false
|
|
||||||
process.once('disconnect', () => {
|
|
||||||
isParentProcessDisconnect = true
|
|
||||||
})
|
|
||||||
const rerunInterval = (() => {
|
const rerunInterval = (() => {
|
||||||
let v = Number(process.env.MAIN_BOSSGEEKGO_RERUN_INTERVAL)
|
let v = Number(process.env.MAIN_BOSSGEEKGO_RERUN_INTERVAL)
|
||||||
if (isNaN(v)) {
|
if (isNaN(v)) {
|
||||||
@@ -457,19 +451,22 @@ const rerunInterval = (() => {
|
|||||||
return v
|
return v
|
||||||
})()
|
})()
|
||||||
|
|
||||||
let pipe
|
async function checkShouldExit () {
|
||||||
try {
|
const shouldExitResponse = await sendToDaemon(
|
||||||
pipe = fs.createWriteStream(null, { fd: 3 })
|
{
|
||||||
} catch {
|
type: 'check-should-exit',
|
||||||
console.warn('pipe is not available')
|
workerId: process.env.GEEKGEEKRUND_WORKER_ID,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
needCallback: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return shouldExitResponse?.shouldExit
|
||||||
}
|
}
|
||||||
export async function runEntry() {
|
export async function runEntry() {
|
||||||
process.on('disconnect', () => {
|
connectToDaemon()
|
||||||
app.exit()
|
|
||||||
})
|
|
||||||
app.dock?.hide()
|
app.dock?.hide()
|
||||||
|
while (true) {
|
||||||
while (!isParentProcessDisconnect) {
|
|
||||||
try {
|
try {
|
||||||
await mainLoop()
|
await mainLoop()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -479,31 +476,45 @@ export async function runEntry() {
|
|||||||
} catch {
|
} catch {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
// handle error
|
const shouldExit = await checkShouldExit()
|
||||||
if (
|
if (shouldExit) {
|
||||||
err instanceof Error &&
|
app.exit()
|
||||||
['LOGIN_STATUS_INVALID', 'ACCESS_IS_DENIED', 'ERR_INTERNET_DISCONNECTED'].includes(
|
return
|
||||||
err.message
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
pipeWriteRegardlessError(
|
|
||||||
pipe,
|
|
||||||
JSON.stringify({
|
|
||||||
type: err.message
|
|
||||||
}) + '\r\n'
|
|
||||||
)
|
|
||||||
process.exit(1)
|
|
||||||
}
|
}
|
||||||
if (err instanceof Error && err.message === 'CANNOT_FIND_A_USABLE_MODEL') {
|
// handle error
|
||||||
gtag('cannot_find_a_usable_model')
|
if (err instanceof Error) {
|
||||||
await dialog.showMessageBox({
|
if (err.message.includes('LOGIN_STATUS_INVALID')) {
|
||||||
type: 'error',
|
await dialog.showMessageBox({
|
||||||
message:
|
type: `error`,
|
||||||
'未找到可以使用的模型,请确定您所配置的模型均可使用。重启本程序或许可以解决这个问题',
|
message: `登录状态无效`,
|
||||||
buttons: ['退出']
|
detail: `请重新登录Boss直聘`
|
||||||
})
|
})
|
||||||
process.exit(0)
|
process.exit(AUTO_CHAT_ERROR_EXIT_CODE.LOGIN_STATUS_INVALID)
|
||||||
break;
|
break
|
||||||
|
}
|
||||||
|
if (err.message.includes('ERR_INTERNET_DISCONNECTED')) {
|
||||||
|
process.exit(AUTO_CHAT_ERROR_EXIT_CODE.ERR_INTERNET_DISCONNECTED)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (err.message.includes('ACCESS_IS_DENIED')) {
|
||||||
|
process.exit(AUTO_CHAT_ERROR_EXIT_CODE.ACCESS_IS_DENIED)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (err.message.includes(`Could not find Chrome`) || err.message.includes(`no executable was found`)) {
|
||||||
|
process.exit(AUTO_CHAT_ERROR_EXIT_CODE.PUPPETEER_IS_NOT_EXECUTABLE)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (err.message === 'CANNOT_FIND_A_USABLE_MODEL') {
|
||||||
|
gtag('cannot_find_a_usable_model')
|
||||||
|
await dialog.showMessageBox({
|
||||||
|
type: 'error',
|
||||||
|
message:
|
||||||
|
'未找到可以使用的模型,请确定您所配置的模型均可使用。重启本程序或许可以解决这个问题',
|
||||||
|
buttons: ['退出']
|
||||||
|
})
|
||||||
|
process.exit(AUTO_CHAT_ERROR_EXIT_CODE.LLM_UNAVAILABLE)
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
pageMapByName['boss'] = null
|
pageMapByName['boss'] = null
|
||||||
@@ -514,8 +525,6 @@ export async function runEntry() {
|
|||||||
process.exit(0)
|
process.exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
attachListenerForKillSelfOnParentExited()
|
|
||||||
|
|
||||||
process.once('uncaughtException', (error) => {
|
process.once('uncaughtException', (error) => {
|
||||||
console.error('uncaughtException', error)
|
console.error('uncaughtException', error)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
@@ -525,6 +534,10 @@ process.once('unhandledRejection', (error) => {
|
|||||||
process.exit(1)
|
process.exit(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
process.once('disconnect', () => {
|
||||||
|
process.exit(0)
|
||||||
|
})
|
||||||
|
|
||||||
async function storeStorage(page) {
|
async function storeStorage(page) {
|
||||||
const [cookies, localStorage] = await Promise.all([
|
const [cookies, localStorage] = await Promise.all([
|
||||||
page.cookies(),
|
page.cookies(),
|
||||||
|
|||||||
@@ -14,13 +14,6 @@ const runMode = commandlineArgs['mode'];
|
|||||||
waitForProcessHandShakeAndRunAutoChat()
|
waitForProcessHandShakeAndRunAutoChat()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'geekAutoStartWithBossDaemon': {
|
|
||||||
const { runAutoChatWithDaemon } = await import(
|
|
||||||
'./flow/GEEK_AUTO_START_CHAT_WITH_BOSS_DAEMON/index'
|
|
||||||
)
|
|
||||||
runAutoChatWithDaemon()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'checkAndDownloadDependenciesForInit': {
|
case 'checkAndDownloadDependenciesForInit': {
|
||||||
const { checkAndDownloadDependenciesForInit } = await import(
|
const { checkAndDownloadDependenciesForInit } = await import(
|
||||||
'./flow/CHECK_AND_DOWNLOAD_DEPENDENCIES/index'
|
'./flow/CHECK_AND_DOWNLOAD_DEPENDENCIES/index'
|
||||||
|
|||||||
+9
-8
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="geek-auto-start-chat-with-boss__running-status">
|
<div class="read-no-reply-auto-reminder__running-status">
|
||||||
<FlyingCompanyLogoList class="flying-company-logo-list" />
|
<FlyingCompanyLogoList class="flying-company-logo-list" />
|
||||||
<div class="tip">
|
<div class="tip">
|
||||||
<article>
|
<article>
|
||||||
@@ -23,7 +23,7 @@ const router = useRouter()
|
|||||||
|
|
||||||
const handleStopButtonClick = async () => {
|
const handleStopButtonClick = async () => {
|
||||||
gtagRenderer('rnrr_stop_button_clicked')
|
gtagRenderer('rnrr_stop_button_clicked')
|
||||||
ipcRenderer.invoke('stop-geek-auto-start-chat-with-boss')
|
ipcRenderer.invoke('stop-read-no-reply-auto-reminder')
|
||||||
}
|
}
|
||||||
|
|
||||||
const isStopping = ref(false)
|
const isStopping = ref(false)
|
||||||
@@ -31,23 +31,24 @@ const handleStopping = () => {
|
|||||||
gtagRenderer('rnrr_become_stopping')
|
gtagRenderer('rnrr_become_stopping')
|
||||||
isStopping.value = true
|
isStopping.value = true
|
||||||
}
|
}
|
||||||
ipcRenderer.once('geek-auto-start-chat-with-boss-stopping', handleStopping)
|
ipcRenderer.once('read-no-reply-auto-reminder-stopping', handleStopping)
|
||||||
|
|
||||||
const handleStopped = () => {
|
const handleStopped = () => {
|
||||||
gtagRenderer('rnrr_become_stopped')
|
gtagRenderer('rnrr_become_stopped')
|
||||||
router.replace('/main-layout/ReadNoReplyReminder')
|
router.replace('/main-layout/ReadNoReplyReminder')
|
||||||
}
|
}
|
||||||
ipcRenderer.once('geek-auto-start-chat-with-boss-stopped', handleStopped)
|
ipcRenderer.once('read-no-reply-auto-reminder-stopped', handleStopped)
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
ipcRenderer.removeListener('geek-auto-start-chat-with-boss-stopped', handleStopped)
|
ipcRenderer.removeListener('read-no-reply-auto-reminder-stopped', handleStopped)
|
||||||
ipcRenderer.removeListener('geek-auto-start-chat-with-boss-stopping', handleStopping)
|
ipcRenderer.removeListener('read-no-reply-auto-reminder-stopping', handleStopping)
|
||||||
})
|
})
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
try {
|
try {
|
||||||
await electron.ipcRenderer.invoke('run-read-no-reply-auto-reminder')
|
await electron.ipcRenderer.invoke('run-read-no-reply-auto-reminder')
|
||||||
} catch (err) {
|
}
|
||||||
|
catch (err) {
|
||||||
if (err instanceof Error && err.message.includes('NEED_TO_CHECK_RUNTIME_DEPENDENCIES')) {
|
if (err instanceof Error && err.message.includes('NEED_TO_CHECK_RUNTIME_DEPENDENCIES')) {
|
||||||
gtagRenderer('rnrr_cannot_run_for_corrupt')
|
gtagRenderer('rnrr_cannot_run_for_corrupt')
|
||||||
ElMessage.error({
|
ElMessage.error({
|
||||||
@@ -62,7 +63,7 @@ onMounted(async () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.geek-auto-start-chat-with-boss__running-status {
|
.read-no-reply-auto-reminder__running-status {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { ref, onMounted, onUnmounted } from 'vue'
|
|||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
const { ipcRenderer } = electron
|
||||||
|
|
||||||
const currentStatus = ref('')
|
const currentStatus = ref('')
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -40,4 +41,13 @@ onMounted(() => {
|
|||||||
console.error(err)
|
console.error(err)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const needToCheckRuntimeDependenciesHandler = () => {
|
||||||
|
router.replace('/')
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcRenderer.on('need-to-check-runtime-dependencies', needToCheckRuntimeDependenciesHandler)
|
||||||
|
onUnmounted(() => {
|
||||||
|
ipcRenderer.removeListener('need-to-check-runtime-dependencies', needToCheckRuntimeDependenciesHandler)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user