claw-subagent-service 0.0.17 → 0.0.18
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/cli.js +34 -30
- package/package.json +1 -1
- package/scripts/install-silent.js +39 -29
- package/scripts/post-install.js +36 -44
- package/scripts/pre-uninstall.js +11 -0
- package/service/modules/service-manager.js +33 -14
- package/version.json +2 -2
package/cli.js
CHANGED
|
@@ -50,31 +50,39 @@ function installService() {
|
|
|
50
50
|
const execPath = process.execPath;
|
|
51
51
|
|
|
52
52
|
if (platform === 'win32') {
|
|
53
|
-
// Windows: 使用
|
|
54
|
-
console.log('[CLI] 使用
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (err2) {
|
|
66
|
-
console.error(`[CLI] 服务安装失败: ${err2.message}`);
|
|
67
|
-
console.error(`[CLI] stdout: ${stdout}`);
|
|
68
|
-
console.error(`[CLI] stderr: ${stderr}`);
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
console.log(`[CLI] 注册服务输出: ${stdout}`);
|
|
53
|
+
// Windows: 使用 node-windows 注册服务(正确处理路径引号)
|
|
54
|
+
console.log('[CLI] 使用 node-windows 安装服务...');
|
|
55
|
+
try {
|
|
56
|
+
const Service = require('node-windows').Service;
|
|
57
|
+
const svc = new Service({
|
|
58
|
+
name: SERVICE_NAME,
|
|
59
|
+
description: 'OpenClaw Guard',
|
|
60
|
+
script: DAEMON_PATH,
|
|
61
|
+
nodeOptions: ['--harmony', '--max_old_space_size=4096']
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
svc.on('install', () => {
|
|
72
65
|
console.log('[CLI] 服务安装成功');
|
|
73
|
-
|
|
74
|
-
if (err3) console.error(`[CLI] 启动服务失败: ${err3.message}`);
|
|
75
|
-
});
|
|
66
|
+
svc.start();
|
|
76
67
|
});
|
|
77
|
-
|
|
68
|
+
|
|
69
|
+
svc.on('start', () => {
|
|
70
|
+
console.log('[CLI] 服务已启动');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
svc.on('error', (err) => {
|
|
74
|
+
console.error(`[CLI] 服务安装失败: ${err.message}`);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
svc.on('alreadyinstalled', () => {
|
|
78
|
+
console.log('[CLI] 服务已存在,尝试启动...');
|
|
79
|
+
svc.start();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
svc.install();
|
|
83
|
+
} catch (err) {
|
|
84
|
+
console.error(`[CLI] 安装服务异常: ${err.message}`);
|
|
85
|
+
}
|
|
78
86
|
} else if (platform === 'linux') {
|
|
79
87
|
// Linux: systemd
|
|
80
88
|
const execStart = `/usr/bin/node ${DAEMON_PATH}`;
|
|
@@ -145,19 +153,15 @@ function uninstallService() {
|
|
|
145
153
|
const platform = process.platform;
|
|
146
154
|
|
|
147
155
|
if (platform === 'win32') {
|
|
148
|
-
// Windows:
|
|
156
|
+
// Windows: 使用 node-windows 卸载服务
|
|
149
157
|
try {
|
|
150
|
-
if (process.pkg) throw new Error('pkg 环境');
|
|
151
158
|
const Service = require('node-windows').Service;
|
|
152
159
|
const svc = new Service({ name: SERVICE_NAME, script: DAEMON_PATH });
|
|
153
160
|
svc.on('uninstall', () => console.log('[CLI] 服务卸载成功'));
|
|
161
|
+
svc.on('error', (err) => console.error(`[CLI] 卸载失败: ${err.message}`));
|
|
154
162
|
svc.uninstall();
|
|
155
163
|
} catch (err) {
|
|
156
|
-
console.
|
|
157
|
-
exec(`sc.exe stop ${SERVICE_NAME} 2>nul & sc.exe delete ${SERVICE_NAME}`, (err2) => {
|
|
158
|
-
if (err2) console.error(`[CLI] 卸载失败: ${err2.message}`);
|
|
159
|
-
else console.log('[CLI] 服务卸载成功');
|
|
160
|
-
});
|
|
164
|
+
console.error(`[CLI] 卸载服务异常: ${err.message}`);
|
|
161
165
|
}
|
|
162
166
|
} else if (platform === 'linux') {
|
|
163
167
|
exec(`systemctl stop ${SERVICE_NAME} && systemctl disable ${SERVICE_NAME} && rm -f /etc/systemd/system/${SERVICE_NAME}.service && systemctl daemon-reload`, (err) => {
|
package/package.json
CHANGED
|
@@ -44,45 +44,55 @@ if (platform === 'win32') {
|
|
|
44
44
|
// 兜底超时
|
|
45
45
|
setTimeout(() => finish(1, '安装操作超时'), 60000);
|
|
46
46
|
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
47
|
+
// 使用 node-windows 注册服务(正确处理路径引号)
|
|
48
|
+
try {
|
|
49
|
+
const Service = require('node-windows').Service;
|
|
50
|
+
const svc = new Service({
|
|
51
|
+
name: SERVICE_NAME,
|
|
52
|
+
description: 'Node.js 静默后台服务(开机自启/崩溃自动恢复/自动更新)',
|
|
53
|
+
script: DAEMON_PATH,
|
|
54
|
+
wait: 2,
|
|
55
|
+
grow: 0.5,
|
|
56
|
+
abortOnError: false
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
svc.on('install', () => {
|
|
60
|
+
log('服务安装成功,正在启动...');
|
|
61
|
+
svc.start();
|
|
62
|
+
|
|
63
|
+
const cmd = `sc.exe failure "${SERVICE_NAME}" reset= 0 actions= restart/0/restart/0/restart/0`;
|
|
64
|
+
exec(cmd, (err) => {
|
|
64
65
|
if (err) log(`设置恢复策略失败: ${err.message}`);
|
|
65
66
|
else log('恢复策略已设置:服务崩溃后系统自动无限重启');
|
|
66
67
|
});
|
|
67
68
|
|
|
68
|
-
// 设置自动启动
|
|
69
69
|
exec(`sc.exe config "${SERVICE_NAME}" start= auto`, (err) => {
|
|
70
70
|
if (err) log(`设置自动启动失败: ${err.message}`);
|
|
71
71
|
else log('启动类型已设为:自动');
|
|
72
72
|
});
|
|
73
|
+
});
|
|
73
74
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
log(`启动服务失败: ${err.message}`);
|
|
78
|
-
finish(1, `启动服务失败: ${err.message}`);
|
|
79
|
-
} else {
|
|
80
|
-
log('服务已启动');
|
|
81
|
-
finish(0, '服务安装成功并已启动');
|
|
82
|
-
}
|
|
83
|
-
});
|
|
75
|
+
svc.on('alreadyinstalled', () => {
|
|
76
|
+
log('服务已存在,尝试启动...');
|
|
77
|
+
svc.start();
|
|
84
78
|
});
|
|
85
|
-
|
|
79
|
+
|
|
80
|
+
svc.on('start', () => {
|
|
81
|
+
log('服务已启动');
|
|
82
|
+
finish(0);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
svc.on('error', (err) => {
|
|
86
|
+
log(`安装错误: ${err.message}`);
|
|
87
|
+
finish(1);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
log('开始安装服务...');
|
|
91
|
+
svc.install();
|
|
92
|
+
} catch (err) {
|
|
93
|
+
log(`安装异常: ${err.message}`);
|
|
94
|
+
finish(1);
|
|
95
|
+
}
|
|
86
96
|
});
|
|
87
97
|
} else if (platform === 'linux') {
|
|
88
98
|
const serviceFile = `/etc/systemd/system/${SERVICE_NAME}.service`;
|
package/scripts/post-install.js
CHANGED
|
@@ -2,28 +2,26 @@
|
|
|
2
2
|
* npm postinstall 钩子
|
|
3
3
|
* 全局安装完成后自动注册并启动 Windows 服务
|
|
4
4
|
*/
|
|
5
|
-
const {
|
|
5
|
+
const { execSync } = require('child_process');
|
|
6
6
|
const path = require('path');
|
|
7
7
|
|
|
8
8
|
const SERVICE_NAME = 'claw-subagent-service';
|
|
9
9
|
const DAEMON_PATH = path.join(__dirname, '..', 'service', 'daemon.js');
|
|
10
10
|
|
|
11
11
|
function isGlobalInstall() {
|
|
12
|
-
// 方法1: 检查 __dirname 是否在 node_modules 下且不在当前工作目录下
|
|
13
12
|
const pkgPath = path.normalize(__dirname).toLowerCase();
|
|
14
13
|
const cwd = process.cwd().toLowerCase();
|
|
15
14
|
const inNodeModules = pkgPath.includes('node_modules');
|
|
16
15
|
const notInCwd = !pkgPath.startsWith(cwd);
|
|
17
16
|
|
|
18
|
-
console.log(`[postinstall] 安装检测: pkgPath=${pkgPath}, cwd=${cwd}`);
|
|
19
17
|
console.log(`[postinstall] 安装检测: inNodeModules=${inNodeModules}, notInCwd=${notInCwd}`);
|
|
20
18
|
|
|
21
19
|
if (inNodeModules && notInCwd) {
|
|
22
|
-
console.log('[postinstall]
|
|
20
|
+
console.log('[postinstall] 检测到全局安装');
|
|
23
21
|
return true;
|
|
24
22
|
}
|
|
25
23
|
|
|
26
|
-
//
|
|
24
|
+
// 兜底 - postinstall 只在安装时触发,默认按全局安装处理
|
|
27
25
|
console.log('[postinstall] 兜底检测通过,默认按全局安装处理');
|
|
28
26
|
return true;
|
|
29
27
|
}
|
|
@@ -40,56 +38,50 @@ function isWindowsAdmin() {
|
|
|
40
38
|
|
|
41
39
|
function installAndStartService() {
|
|
42
40
|
if (process.platform !== 'win32') {
|
|
43
|
-
console.log(`[postinstall]
|
|
41
|
+
console.log(`[postinstall] 跳过(非 Windows: ${process.platform})`);
|
|
44
42
|
return;
|
|
45
43
|
}
|
|
46
44
|
|
|
47
45
|
if (!isWindowsAdmin()) {
|
|
48
|
-
console.log('[postinstall]
|
|
49
|
-
console.log('[postinstall]
|
|
46
|
+
console.log('[postinstall] 非管理员权限,跳过自动注册');
|
|
47
|
+
console.log('[postinstall] 请运行: claw-subagent-service --install');
|
|
50
48
|
return;
|
|
51
49
|
}
|
|
52
50
|
|
|
53
51
|
console.log('[postinstall] 正在注册系统服务...');
|
|
54
52
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
console.
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
console.log(`[postinstall] 注册服务输出: ${stdout}`);
|
|
75
|
-
console.log('[postinstall] 服务注册成功');
|
|
76
|
-
|
|
77
|
-
// 设置恢复策略
|
|
78
|
-
exec(`sc.exe failure "${SERVICE_NAME}" reset= 0 actions= restart/0/restart/0/restart/0`, (err) => {
|
|
79
|
-
if (!err) console.log('[postinstall] 恢复策略已设置');
|
|
53
|
+
try {
|
|
54
|
+
// 使用 node-windows 注册服务(正确处理路径引号)
|
|
55
|
+
const Service = require('node-windows').Service;
|
|
56
|
+
const svc = new Service({
|
|
57
|
+
name: SERVICE_NAME,
|
|
58
|
+
description: 'OpenClaw Guard',
|
|
59
|
+
script: DAEMON_PATH,
|
|
60
|
+
nodeOptions: ['--harmony', '--max_old_space_size=4096']
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
svc.on('install', () => {
|
|
64
|
+
console.log('[postinstall] 服务注册成功,正在启动...');
|
|
65
|
+
svc.start();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
svc.on('start', () => {
|
|
69
|
+
console.log('[postinstall] 服务已启动');
|
|
80
70
|
});
|
|
81
71
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (err) {
|
|
85
|
-
console.error(`[postinstall] 启动服务失败: ${err.message}`);
|
|
86
|
-
console.error(`[postinstall] stdout: ${stdout}`);
|
|
87
|
-
console.error(`[postinstall] stderr: ${stderr}`);
|
|
88
|
-
} else {
|
|
89
|
-
console.log('[postinstall] 服务已启动');
|
|
90
|
-
}
|
|
72
|
+
svc.on('error', (err) => {
|
|
73
|
+
console.error(`[postinstall] 服务错误: ${err.message}`);
|
|
91
74
|
});
|
|
92
|
-
|
|
75
|
+
|
|
76
|
+
svc.on('alreadyinstalled', () => {
|
|
77
|
+
console.log('[postinstall] 服务已存在,尝试启动...');
|
|
78
|
+
svc.start();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
svc.install();
|
|
82
|
+
} catch (err) {
|
|
83
|
+
console.error(`[postinstall] 注册服务异常: ${err.message}`);
|
|
84
|
+
}
|
|
93
85
|
}
|
|
94
86
|
|
|
95
87
|
// 主逻辑
|
|
@@ -99,5 +91,5 @@ if (isGlobalInstall()) {
|
|
|
99
91
|
console.log('[postinstall] 准备自动注册服务...');
|
|
100
92
|
installAndStartService();
|
|
101
93
|
} else {
|
|
102
|
-
console.log('[postinstall]
|
|
94
|
+
console.log('[postinstall] 本地安装模式,跳过自动注册');
|
|
103
95
|
}
|
package/scripts/pre-uninstall.js
CHANGED
|
@@ -2,15 +2,26 @@
|
|
|
2
2
|
* npm preuninstall 钩子
|
|
3
3
|
* 在卸载旧版本前停止并删除 Windows 服务,释放文件锁
|
|
4
4
|
*/
|
|
5
|
+
const path = require('path');
|
|
5
6
|
const { execSync } = require('child_process');
|
|
6
7
|
|
|
7
8
|
const SERVICES = ['SilentNodeService', 'claw-subagent-service'];
|
|
9
|
+
const DAEMON_PATH = path.join(__dirname, '..', 'service', 'daemon.js');
|
|
8
10
|
|
|
9
11
|
if (process.platform === 'win32') {
|
|
10
12
|
for (const name of SERVICES) {
|
|
11
13
|
try {
|
|
12
14
|
execSync(`net stop "${name}" 2>nul`, { stdio: 'ignore', timeout: 10000 });
|
|
13
15
|
} catch (e) {}
|
|
16
|
+
|
|
17
|
+
// 使用 node-windows 卸载服务(删除 wrapper 文件,释放文件锁)
|
|
18
|
+
try {
|
|
19
|
+
const Service = require('node-windows').Service;
|
|
20
|
+
const svc = new Service({ name, script: DAEMON_PATH });
|
|
21
|
+
svc.uninstall();
|
|
22
|
+
} catch (e) {}
|
|
23
|
+
|
|
24
|
+
// 兜底 - 使用 sc.exe 删除服务
|
|
14
25
|
try {
|
|
15
26
|
execSync(`sc.exe delete "${name}" 2>nul`, { stdio: 'ignore', timeout: 10000 });
|
|
16
27
|
} catch (e) {}
|
|
@@ -155,25 +155,44 @@ class ServiceManager {
|
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
// Windows 服务安装(使用
|
|
158
|
+
// Windows 服务安装(使用 node-windows,正确处理路径引号)
|
|
159
159
|
async installWindows() {
|
|
160
|
-
|
|
161
|
-
|
|
160
|
+
return new Promise((resolve, reject) => {
|
|
161
|
+
try {
|
|
162
|
+
const Service = require('node-windows').Service;
|
|
163
|
+
const svc = new Service({
|
|
164
|
+
name: this.serviceName,
|
|
165
|
+
description: this.serviceDesc,
|
|
166
|
+
script: this.scriptPath,
|
|
167
|
+
nodeOptions: ['--harmony', '--max_old_space_size=4096']
|
|
168
|
+
});
|
|
162
169
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
170
|
+
svc.on('install', () => {
|
|
171
|
+
this.log?.info('[ServiceManager] Windows 服务注册成功');
|
|
172
|
+
svc.start();
|
|
173
|
+
});
|
|
166
174
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
175
|
+
svc.on('start', () => {
|
|
176
|
+
this.log?.info('[ServiceManager] Windows 服务已启动');
|
|
177
|
+
resolve(true);
|
|
178
|
+
});
|
|
171
179
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
180
|
+
svc.on('error', (err) => {
|
|
181
|
+
this.log?.error(`[ServiceManager] Windows 服务安装失败: ${err.message}`);
|
|
182
|
+
reject(err);
|
|
183
|
+
});
|
|
175
184
|
|
|
176
|
-
|
|
185
|
+
svc.on('alreadyinstalled', () => {
|
|
186
|
+
this.log?.info('[ServiceManager] 服务已存在,尝试启动...');
|
|
187
|
+
svc.start();
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
svc.install();
|
|
191
|
+
} catch (err) {
|
|
192
|
+
this.log?.error(`[ServiceManager] 安装异常: ${err.message}`);
|
|
193
|
+
reject(err);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
177
196
|
}
|
|
178
197
|
|
|
179
198
|
// Linux 服务安装 (systemd)
|
package/version.json
CHANGED