claw-subagent-service 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -0
- package/cli.js +254 -0
- package/command/linux/restart.sh +98 -0
- package/command/linux/start.sh +101 -0
- package/command/linux/status.sh +140 -0
- package/command/linux/stop.sh +112 -0
- package/command/win/restart.bat +39 -0
- package/command/win/start.bat +65 -0
- package/command/win/status.bat +52 -0
- package/command/win/stop.bat +55 -0
- package/command/win/windows/345/220/257/345/212/250/350/204/232/346/234/254 +0 -0
- package/package.json +37 -0
- package/scripts/install-silent.js +167 -0
- package/scripts/uninstall.js +61 -0
- package/service/daemon.js +189 -0
- package/service/logger.js +31 -0
- package/service/modules/auth.js +17 -0
- package/service/modules/business-message-handler.js +118 -0
- package/service/modules/command-handler.js +152 -0
- package/service/modules/config.js +44 -0
- package/service/modules/dashboard-collector.js +588 -0
- package/service/modules/heartbeat-dashboard.js +153 -0
- package/service/modules/mac-address.js +15 -0
- package/service/modules/message-processor-example.js +72 -0
- package/service/modules/message-processor.js +62 -0
- package/service/modules/normal-message-handler.js +60 -0
- package/service/modules/openclaw-control.js +128 -0
- package/service/modules/openclaw-enum.js +48 -0
- package/service/modules/opencode-service.js +199 -0
- package/service/modules/opencode-starter.js +194 -0
- package/service/modules/port-checker.js +31 -0
- package/service/modules/rongyun-message-handler.js +250 -0
- package/service/modules/rongyun-message-sender.js +157 -0
- package/service/modules/rongyun-message-types.js +28 -0
- package/service/modules/script-executor.js +550 -0
- package/service/modules/service-manager.js +319 -0
- package/service/modules/structured-message-router.js +118 -0
- package/service/rongcloud/env-polyfill.js +95 -0
- package/service/rongcloud/index.js +19 -0
- package/service/rongcloud/message-handler.js +147 -0
- package/service/rongcloud/message-types.js +22 -0
- package/service/rongcloud/openclaw-client.js +98 -0
- package/service/rongcloud/openclaw-config.js +108 -0
- package/service/rongcloud/rongcloud-client.js +273 -0
- package/service/rongcloud/types.js +9 -0
- package/service/updater.js +348 -0
- package/service/worker.js +376 -0
- package/version.json +4 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# OpenClaw 服务停止脚本
|
|
4
|
+
# 用法: ./stop.sh [选项]
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
# 颜色定义
|
|
9
|
+
RED='\033[0;31m'
|
|
10
|
+
GREEN='\033[0;32m'
|
|
11
|
+
YELLOW='\033[1;33m'
|
|
12
|
+
NC='\033[0m' # No Color
|
|
13
|
+
|
|
14
|
+
# 日志函数
|
|
15
|
+
log_info() {
|
|
16
|
+
echo -e "${GREEN}[INFO]${NC} $1"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
log_warn() {
|
|
20
|
+
echo -e "${YELLOW}[WARN]${NC} $1"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
log_error() {
|
|
24
|
+
echo -e "${RED}[ERROR]${NC} $1"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# 服务名称
|
|
28
|
+
SERVICE_NAME="openclaw-gateway.service"
|
|
29
|
+
|
|
30
|
+
# 检查服务是否存在
|
|
31
|
+
check_service() {
|
|
32
|
+
if ! systemctl --user list-unit-files "$SERVICE_NAME" &>/dev/null; then
|
|
33
|
+
log_error "服务 $SERVICE_NAME 不存在。"
|
|
34
|
+
exit 1
|
|
35
|
+
fi
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
# 显示帮助信息
|
|
39
|
+
show_help() {
|
|
40
|
+
echo "OpenClaw 服务停止脚本"
|
|
41
|
+
echo ""
|
|
42
|
+
echo "用法: $0 [选项]"
|
|
43
|
+
echo ""
|
|
44
|
+
echo "选项:"
|
|
45
|
+
echo " -f, --force 强制停止服务"
|
|
46
|
+
echo " -h, --help 显示此帮助信息"
|
|
47
|
+
echo ""
|
|
48
|
+
echo "示例:"
|
|
49
|
+
echo " $0 正常停止服务"
|
|
50
|
+
echo " $0 -f 强制停止服务"
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# 主函数
|
|
54
|
+
main() {
|
|
55
|
+
local force=""
|
|
56
|
+
|
|
57
|
+
# 解析命令行参数
|
|
58
|
+
while [[ $# -gt 0 ]]; do
|
|
59
|
+
case $1 in
|
|
60
|
+
-f|--force)
|
|
61
|
+
force="--force"
|
|
62
|
+
shift
|
|
63
|
+
;;
|
|
64
|
+
-h|--help)
|
|
65
|
+
show_help
|
|
66
|
+
exit 0
|
|
67
|
+
;;
|
|
68
|
+
*)
|
|
69
|
+
log_error "未知选项: $1"
|
|
70
|
+
show_help
|
|
71
|
+
exit 1
|
|
72
|
+
;;
|
|
73
|
+
esac
|
|
74
|
+
done
|
|
75
|
+
|
|
76
|
+
# 检查服务是否存在
|
|
77
|
+
check_service
|
|
78
|
+
|
|
79
|
+
# 检查服务状态
|
|
80
|
+
log_info "检查 OpenClaw 服务状态..."
|
|
81
|
+
if systemctl --user is-active --quiet "$SERVICE_NAME"; then
|
|
82
|
+
log_info "OpenClaw 服务正在运行,准备停止..."
|
|
83
|
+
else
|
|
84
|
+
log_warn "OpenClaw 服务未在运行。"
|
|
85
|
+
exit 0
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
# 停止服务
|
|
89
|
+
log_info "正在停止 OpenClaw 服务..."
|
|
90
|
+
|
|
91
|
+
if systemctl --user stop "$SERVICE_NAME"; then
|
|
92
|
+
log_info "OpenClaw 服务停止成功!"
|
|
93
|
+
|
|
94
|
+
# 等待服务完全停止
|
|
95
|
+
sleep 2
|
|
96
|
+
|
|
97
|
+
# 验证服务状态
|
|
98
|
+
if systemctl --user is-active --quiet "$SERVICE_NAME"; then
|
|
99
|
+
log_warn "服务可能仍在运行,请检查进程。"
|
|
100
|
+
systemctl --user status "$SERVICE_NAME" --no-pager
|
|
101
|
+
else
|
|
102
|
+
log_info "服务已成功停止。"
|
|
103
|
+
log_info "Success"
|
|
104
|
+
fi
|
|
105
|
+
else
|
|
106
|
+
log_error "OpenClaw 服务停止失败!"
|
|
107
|
+
exit 1
|
|
108
|
+
fi
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
# 执行主函数
|
|
112
|
+
main "$@"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
echo ==========================================
|
|
3
|
+
echo OpenClaw Service Restart Script
|
|
4
|
+
echo ==========================================
|
|
5
|
+
echo.
|
|
6
|
+
|
|
7
|
+
REM Get the directory where this script is located
|
|
8
|
+
set "SCRIPT_DIR=%~dp0"
|
|
9
|
+
|
|
10
|
+
echo [INFO] Step 1: Stopping service...
|
|
11
|
+
call "%SCRIPT_DIR%stop.bat"
|
|
12
|
+
if errorlevel 1 (
|
|
13
|
+
echo [WARN] Stop returned error, continuing...
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
echo.
|
|
17
|
+
echo [INFO] Step 2: Waiting for service to stop...
|
|
18
|
+
timeout /t 3 /nobreak >nul
|
|
19
|
+
|
|
20
|
+
REM Verify stopped
|
|
21
|
+
openclaw gateway status 2>&1 | findstr /C:"RPC probe: ok" >nul
|
|
22
|
+
if errorlevel 1 (
|
|
23
|
+
echo [OK] Service stopped
|
|
24
|
+
) else (
|
|
25
|
+
echo [WARN] Service may still be running, proceeding anyway
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
echo.
|
|
29
|
+
echo [INFO] Step 3: Starting service...
|
|
30
|
+
call "%SCRIPT_DIR%start.bat"
|
|
31
|
+
if errorlevel 1 (
|
|
32
|
+
echo [ERROR] Start failed
|
|
33
|
+
exit /b 1
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
echo.
|
|
37
|
+
echo [OK] Restart completed successfully
|
|
38
|
+
echo Success
|
|
39
|
+
exit /b 0
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
echo ==========================================
|
|
3
|
+
echo OpenClaw Service Start Script
|
|
4
|
+
echo ==========================================
|
|
5
|
+
echo.
|
|
6
|
+
|
|
7
|
+
REM Check if openclaw exists
|
|
8
|
+
where openclaw >nul 2>&1
|
|
9
|
+
if errorlevel 1 (
|
|
10
|
+
echo [ERROR] openclaw command not found in PATH
|
|
11
|
+
exit /b 1
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
echo [OK] openclaw command found
|
|
15
|
+
echo.
|
|
16
|
+
|
|
17
|
+
REM Check if service is already running
|
|
18
|
+
netstat -an | findstr ":18789 " | findstr "LISTENING" >nul
|
|
19
|
+
if not errorlevel 1 (
|
|
20
|
+
echo [INFO] OpenClaw is already running
|
|
21
|
+
echo [INFO] Dashboard URL: http://127.0.0.1:18789/
|
|
22
|
+
echo.
|
|
23
|
+
echo Success
|
|
24
|
+
exit /b 0
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
echo [INFO] OpenClaw is not running
|
|
28
|
+
echo.
|
|
29
|
+
echo Starting OpenClaw service...
|
|
30
|
+
echo.
|
|
31
|
+
|
|
32
|
+
REM Start openclaw gateway using START (creates new process, no waiting)
|
|
33
|
+
echo [INFO] Launching openclaw gateway...
|
|
34
|
+
start "OpenClaw Gateway" /min cmd /c "openclaw gateway"
|
|
35
|
+
|
|
36
|
+
echo [OK] Start command sent
|
|
37
|
+
echo.
|
|
38
|
+
echo Waiting for service to start...
|
|
39
|
+
echo.
|
|
40
|
+
|
|
41
|
+
REM Wait loop - max 60 seconds
|
|
42
|
+
REM Using ping for delay instead of timeout (more compatible with redirected stdout)
|
|
43
|
+
set /a count=0
|
|
44
|
+
:LOOP
|
|
45
|
+
set /a count+=1
|
|
46
|
+
if %count% gtr 30 (
|
|
47
|
+
echo [ERROR] Timeout waiting for service to start
|
|
48
|
+
exit /b 1
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
REM Use ping for 2 second delay (more reliable than timeout when stdout is redirected)
|
|
52
|
+
ping -n 3 127.0.0.1 >nul 2>&1
|
|
53
|
+
|
|
54
|
+
REM Check if service is ready
|
|
55
|
+
netstat -an | findstr ":18789 " | findstr "LISTENING" >nul
|
|
56
|
+
if not errorlevel 1 (
|
|
57
|
+
echo [OK] Service is running
|
|
58
|
+
echo [INFO] Dashboard URL: http://127.0.0.1:18789/
|
|
59
|
+
echo.
|
|
60
|
+
echo Success
|
|
61
|
+
exit /b 0
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
echo Waiting... (%count%/30)
|
|
65
|
+
goto LOOP
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
echo ==========================================
|
|
3
|
+
echo OpenClaw Service Status Script
|
|
4
|
+
echo ==========================================
|
|
5
|
+
echo.
|
|
6
|
+
|
|
7
|
+
set QUIET=
|
|
8
|
+
if "%~1"=="-q" set QUIET=1
|
|
9
|
+
if "%~1"=="--quiet" set QUIET=1
|
|
10
|
+
|
|
11
|
+
REM Check if openclaw exists
|
|
12
|
+
where openclaw >nul 2>&1
|
|
13
|
+
if errorlevel 1 (
|
|
14
|
+
if defined QUIET (
|
|
15
|
+
echo not_installed
|
|
16
|
+
) else (
|
|
17
|
+
echo [ERROR] openclaw command not found in PATH
|
|
18
|
+
)
|
|
19
|
+
exit /b 1
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
if defined QUIET (
|
|
23
|
+
netstat -an | findstr ":18789 " | findstr "LISTENING" >nul
|
|
24
|
+
if errorlevel 1 (
|
|
25
|
+
echo stopped
|
|
26
|
+
) else (
|
|
27
|
+
echo running
|
|
28
|
+
)
|
|
29
|
+
exit /b 0
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
echo === OpenClaw Status ===
|
|
33
|
+
echo.
|
|
34
|
+
|
|
35
|
+
REM Check if port 18789 is listening (simple check)
|
|
36
|
+
netstat -an | findstr ":18789 " | findstr "LISTENING" >nul
|
|
37
|
+
if errorlevel 1 (
|
|
38
|
+
echo [INFO] Status: Not running
|
|
39
|
+
echo.
|
|
40
|
+
echo [ERROR] OpenClaw service is not running
|
|
41
|
+
exit /b 1
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
REM Service is running, now get detailed status
|
|
45
|
+
cmd /c openclaw gateway status
|
|
46
|
+
|
|
47
|
+
echo.
|
|
48
|
+
echo ==========================================
|
|
49
|
+
echo [INFO] Status: Running
|
|
50
|
+
echo.
|
|
51
|
+
echo Success
|
|
52
|
+
exit /b 0
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
echo ==========================================
|
|
3
|
+
echo OpenClaw Service Stop Script
|
|
4
|
+
echo ==========================================
|
|
5
|
+
echo.
|
|
6
|
+
|
|
7
|
+
set FORCE=
|
|
8
|
+
if "%~1"=="-f" set FORCE=1
|
|
9
|
+
if "%~1"=="--force" set FORCE=1
|
|
10
|
+
|
|
11
|
+
REM Check if openclaw exists
|
|
12
|
+
where openclaw >nul 2>&1
|
|
13
|
+
if errorlevel 1 (
|
|
14
|
+
echo [ERROR] openclaw command not found in PATH
|
|
15
|
+
exit /b 1
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
REM Check if service is running (using port detection)
|
|
19
|
+
netstat -an | findstr ":18789 " | findstr "LISTENING" >nul
|
|
20
|
+
if errorlevel 1 (
|
|
21
|
+
echo [INFO] OpenClaw is not running
|
|
22
|
+
exit /b 0
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
echo [INFO] OpenClaw is running
|
|
26
|
+
echo [INFO] Stopping service...
|
|
27
|
+
echo.
|
|
28
|
+
|
|
29
|
+
if defined FORCE (
|
|
30
|
+
echo [INFO] Force stopping...
|
|
31
|
+
taskkill /f /im openclaw.exe >nul 2>&1
|
|
32
|
+
taskkill /f /im node.exe >nul 2>&1
|
|
33
|
+
) else (
|
|
34
|
+
REM Simple stop command without extra cmd wrapper
|
|
35
|
+
openclaw gateway stop
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
echo.
|
|
39
|
+
echo [OK] Stop command executed
|
|
40
|
+
echo Waiting for service to stop...
|
|
41
|
+
echo.
|
|
42
|
+
|
|
43
|
+
REM Wait and verify stopped
|
|
44
|
+
timeout /t 3 /nobreak >nul
|
|
45
|
+
|
|
46
|
+
netstat -an | findstr ":18789 " | findstr "LISTENING" >nul
|
|
47
|
+
if errorlevel 1 (
|
|
48
|
+
echo [OK] Service stopped successfully
|
|
49
|
+
echo.
|
|
50
|
+
echo Success
|
|
51
|
+
exit /b 0
|
|
52
|
+
) else (
|
|
53
|
+
echo [WARN] Service may still be running
|
|
54
|
+
exit /b 0
|
|
55
|
+
)
|
|
File without changes
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "claw-subagent-service",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "claw-subagent-service 静默后台服务,开机自启,崩溃自动恢复,支持自动更新和融云消息监听",
|
|
5
|
+
"main": "cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"claw-subagent-service": "cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"install-service": "node scripts/install-silent.js",
|
|
11
|
+
"uninstall-service": "node scripts/uninstall.js",
|
|
12
|
+
"dev": "nodemon cli.js --run",
|
|
13
|
+
"publish:patch": "npm version patch && npm publish",
|
|
14
|
+
"publish:minor": "npm version minor && npm publish",
|
|
15
|
+
"publish:major": "npm version major && npm publish"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"cli.js",
|
|
19
|
+
"service/",
|
|
20
|
+
"scripts/",
|
|
21
|
+
"command/",
|
|
22
|
+
"version.json",
|
|
23
|
+
"README.md"
|
|
24
|
+
],
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@rongcloud/imlib-next": "^5.36.6",
|
|
27
|
+
"axios": "^1.15.2",
|
|
28
|
+
"fake-indexeddb": "^6.2.5",
|
|
29
|
+
"jsdom": "^24.0.0",
|
|
30
|
+
"node-windows": "^1.0.0-beta.8",
|
|
31
|
+
"ws": "^8.16.0"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=14.0.0"
|
|
35
|
+
},
|
|
36
|
+
"license": "MIT"
|
|
37
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
const { Service } = require('node-windows');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { exec } = require('child_process');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
|
|
6
|
+
const ROOT = process.argv[2] || process.env.SILENT_SERVICE_DIR || path.join(__dirname, '..');
|
|
7
|
+
const DAEMON_PATH = path.join(ROOT, 'service', 'daemon.js');
|
|
8
|
+
const SERVICE_NAME = 'SilentNodeService';
|
|
9
|
+
const LOG_FILE = path.join(ROOT, 'logs', 'install.log');
|
|
10
|
+
|
|
11
|
+
if (!fs.existsSync(path.dirname(LOG_FILE))) {
|
|
12
|
+
fs.mkdirSync(path.dirname(LOG_FILE), { recursive: true });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const log = (msg) => {
|
|
16
|
+
const line = `[${new Date().toISOString()}] ${msg}\n`;
|
|
17
|
+
fs.appendFileSync(LOG_FILE, line);
|
|
18
|
+
console.log(msg);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const platform = process.platform;
|
|
22
|
+
|
|
23
|
+
function done(code, label) {
|
|
24
|
+
if (label) log(label);
|
|
25
|
+
setTimeout(() => process.exit(code), 500);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (platform === 'win32') {
|
|
29
|
+
exec('net session', (err) => {
|
|
30
|
+
if (err) {
|
|
31
|
+
const msg = '错误:请以管理员身份运行此脚本';
|
|
32
|
+
fs.writeFileSync(LOG_FILE, `[${new Date().toISOString()}] ${msg}\n`);
|
|
33
|
+
console.error(msg);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let completed = false;
|
|
38
|
+
function finish(code, label) {
|
|
39
|
+
if (!completed) {
|
|
40
|
+
completed = true;
|
|
41
|
+
done(code, label);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 兜底超时
|
|
46
|
+
setTimeout(() => finish(1, '安装操作超时'), 60000);
|
|
47
|
+
|
|
48
|
+
const svc = new Service({
|
|
49
|
+
name: SERVICE_NAME,
|
|
50
|
+
description: 'Node.js 静默后台服务(开机自启/崩溃自动恢复/自动更新)',
|
|
51
|
+
script: DAEMON_PATH,
|
|
52
|
+
wait: 2,
|
|
53
|
+
grow: 0.5,
|
|
54
|
+
abortOnError: false
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
svc.on('install', () => {
|
|
58
|
+
log('服务安装成功,正在启动...');
|
|
59
|
+
svc.start();
|
|
60
|
+
|
|
61
|
+
const cmd = `sc failure "${SERVICE_NAME}" reset= 0 actions= restart/0/restart/0/restart/0`;
|
|
62
|
+
exec(cmd, (err) => {
|
|
63
|
+
if (err) log(`设置恢复策略失败: ${err.message}`);
|
|
64
|
+
else log('恢复策略已设置:服务崩溃后系统自动无限重启');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
exec(`sc config "${SERVICE_NAME}" start= auto`, (err) => {
|
|
68
|
+
if (err) log(`设置自动启动失败: ${err.message}`);
|
|
69
|
+
else log('启动类型已设为:自动');
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
svc.on('alreadyinstalled', () => {
|
|
74
|
+
log('服务已存在,尝试启动...');
|
|
75
|
+
svc.start();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
svc.on('start', () => {
|
|
79
|
+
log('服务已启动');
|
|
80
|
+
finish(0);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
svc.on('error', (err) => {
|
|
84
|
+
log(`安装错误: ${err.message}`);
|
|
85
|
+
finish(1);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
log('开始安装服务...');
|
|
89
|
+
svc.install();
|
|
90
|
+
});
|
|
91
|
+
} else if (platform === 'linux') {
|
|
92
|
+
const serviceFile = `/etc/systemd/system/${SERVICE_NAME}.service`;
|
|
93
|
+
const serviceContent = `[Unit]
|
|
94
|
+
Description=Node.js 静默后台服务(开机自启/崩溃自动恢复/自动更新)
|
|
95
|
+
After=network.target
|
|
96
|
+
|
|
97
|
+
[Service]
|
|
98
|
+
Type=simple
|
|
99
|
+
ExecStart=/usr/bin/node ${DAEMON_PATH}
|
|
100
|
+
Restart=always
|
|
101
|
+
RestartSec=10
|
|
102
|
+
WorkingDirectory=${path.dirname(DAEMON_PATH)}
|
|
103
|
+
Environment="SILENT_SERVICE_DIR=${ROOT}"
|
|
104
|
+
|
|
105
|
+
[Install]
|
|
106
|
+
WantedBy=multi-user.target
|
|
107
|
+
`;
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
fs.writeFileSync(serviceFile, serviceContent);
|
|
111
|
+
exec('systemctl daemon-reload && systemctl enable ' + SERVICE_NAME, (err) => {
|
|
112
|
+
if (err) {
|
|
113
|
+
done(1, `安装失败: ${err.message}`);
|
|
114
|
+
} else {
|
|
115
|
+
log('服务安装成功');
|
|
116
|
+
exec('systemctl start ' + SERVICE_NAME, (err2) => {
|
|
117
|
+
if (err2) {
|
|
118
|
+
done(1, `启动失败: ${err2.message}`);
|
|
119
|
+
} else {
|
|
120
|
+
done(0, '服务已启动');
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
} catch (err) {
|
|
126
|
+
done(1, `安装失败: ${err.message}`);
|
|
127
|
+
}
|
|
128
|
+
} else if (platform === 'darwin') {
|
|
129
|
+
const plistFile = `/Library/LaunchDaemons/${SERVICE_NAME}.plist`;
|
|
130
|
+
const plistContent = `<?xml version="1.0" encoding="UTF-8"?>
|
|
131
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
132
|
+
<plist version="1.0">
|
|
133
|
+
<dict>
|
|
134
|
+
<key>Label</key>
|
|
135
|
+
<string>${SERVICE_NAME}</string>
|
|
136
|
+
<key>ProgramArguments</key>
|
|
137
|
+
<array>
|
|
138
|
+
<string>/usr/local/bin/node</string>
|
|
139
|
+
<string>${DAEMON_PATH}</string>
|
|
140
|
+
</array>
|
|
141
|
+
<key>RunAtLoad</key>
|
|
142
|
+
<true/>
|
|
143
|
+
<key>KeepAlive</key>
|
|
144
|
+
<true/>
|
|
145
|
+
<key>EnvironmentVariables</key>
|
|
146
|
+
<dict>
|
|
147
|
+
<key>SILENT_SERVICE_DIR</key>
|
|
148
|
+
<string>${ROOT}</string>
|
|
149
|
+
</dict>
|
|
150
|
+
</dict>
|
|
151
|
+
</plist>`;
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
fs.writeFileSync(plistFile, plistContent);
|
|
155
|
+
exec(`launchctl load ${plistFile} && launchctl start ${SERVICE_NAME}`, (err) => {
|
|
156
|
+
if (err) {
|
|
157
|
+
done(1, `安装失败: ${err.message}`);
|
|
158
|
+
} else {
|
|
159
|
+
done(0, '服务安装成功并已启动');
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
} catch (err) {
|
|
163
|
+
done(1, `安装失败: ${err.message}`);
|
|
164
|
+
}
|
|
165
|
+
} else {
|
|
166
|
+
done(1, `不支持的平台: ${platform}`);
|
|
167
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const { Service } = require('node-windows');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { exec } = require('child_process');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
|
|
6
|
+
const ROOT = process.argv[2] || process.env.SILENT_SERVICE_DIR || path.join(__dirname, '..');
|
|
7
|
+
const SERVICE_NAME = 'SilentNodeService';
|
|
8
|
+
const platform = process.platform;
|
|
9
|
+
|
|
10
|
+
function done(code, label) {
|
|
11
|
+
if (label) console.log(label);
|
|
12
|
+
setTimeout(() => process.exit(code), 500);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (platform === 'win32') {
|
|
16
|
+
let completed = false;
|
|
17
|
+
function finish(code, label) {
|
|
18
|
+
if (!completed) {
|
|
19
|
+
completed = true;
|
|
20
|
+
done(code, label);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// 兜底超时
|
|
25
|
+
setTimeout(() => finish(1, '卸载操作超时'), 60000);
|
|
26
|
+
|
|
27
|
+
const svc = new Service({
|
|
28
|
+
name: SERVICE_NAME,
|
|
29
|
+
script: path.join(ROOT, 'service', 'daemon.js')
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
svc.on('uninstall', () => {
|
|
33
|
+
finish(0, '服务已卸载完成');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
svc.on('error', (err) => {
|
|
37
|
+
finish(1, `卸载错误: ${err.message}`);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
console.log('正在卸载服务...');
|
|
41
|
+
svc.uninstall();
|
|
42
|
+
} else if (platform === 'linux') {
|
|
43
|
+
exec(`systemctl stop "${SERVICE_NAME}" && systemctl disable "${SERVICE_NAME}" && rm -f /etc/systemd/system/${SERVICE_NAME}.service && systemctl daemon-reload`, (err) => {
|
|
44
|
+
if (err) {
|
|
45
|
+
done(1, `卸载失败: ${err.message}`);
|
|
46
|
+
} else {
|
|
47
|
+
done(0, '服务已卸载');
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
} else if (platform === 'darwin') {
|
|
51
|
+
const plistFile = `/Library/LaunchDaemons/${SERVICE_NAME}.plist`;
|
|
52
|
+
exec(`launchctl stop "${SERVICE_NAME}" && launchctl unload "${plistFile}" && rm -f "${plistFile}"`, (err) => {
|
|
53
|
+
if (err) {
|
|
54
|
+
done(1, `卸载失败: ${err.message}`);
|
|
55
|
+
} else {
|
|
56
|
+
done(0, '服务已卸载');
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
} else {
|
|
60
|
+
done(1, `不支持的平台: ${platform}`);
|
|
61
|
+
}
|