claw-subagent-service 0.0.10 → 0.0.11

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 CHANGED
@@ -50,26 +50,14 @@ function installService() {
50
50
  const execPath = process.execPath;
51
51
 
52
52
  if (platform === 'win32') {
53
- // Windows: 优先使用 node-windows,失败时回退 sc 命令(兼容 pkg 打包)
54
- try {
55
- if (process.pkg) throw new Error('pkg 环境');
56
- const Service = require('node-windows').Service;
57
- const svc = new Service({
58
- name: SERVICE_NAME,
59
- description: 'OpenClaw Guard CLI Client',
60
- script: DAEMON_PATH,
61
- workingdirectory: os.homedir(),
62
- nodeOptions: ['--harmony', '--max_old_space_size=4096']
63
- });
64
- svc.on('install', () => { console.log('[CLI] 服务安装成功'); svc.start(); });
65
- svc.on('error', (err) => { console.error(`[CLI] 服务安装失败: ${err.message}`); });
66
- svc.install();
67
- } catch (err) {
68
- // 回退到 sc 命令(pkg 或无 node-windows 环境)
69
- console.log('[CLI] 使用 sc 命令安装服务...');
70
- const binPath = process.pkg
71
- ? `"${execPath}" --run`
72
- : `"${execPath}" "${DAEMON_PATH}"`;
53
+ // Windows: 使用 sc 命令直接创建服务,避免 node-windows 在包目录生成被锁定的 wrapper
54
+ console.log('[CLI] 使用 sc 命令安装服务...');
55
+ const binPath = process.pkg
56
+ ? `"${execPath}" --run`
57
+ : `"${execPath}" "${DAEMON_PATH}"`;
58
+
59
+ // 先停止并删除旧服务(避免文件锁)
60
+ exec(`net stop ${SERVICE_NAME} 2>nul & sc delete ${SERVICE_NAME} 2>nul`, () => {
73
61
  exec(`sc create ${SERVICE_NAME} binPath= "${binPath}" start= auto displayname= "OpenClaw Guard"`, (err2) => {
74
62
  if (err2) return console.error(`[CLI] 服务安装失败: ${err2.message}`);
75
63
  console.log('[CLI] 服务安装成功');
@@ -77,7 +65,7 @@ function installService() {
77
65
  if (err3) console.error(`[CLI] 启动服务失败: ${err3.message}`);
78
66
  });
79
67
  });
80
- }
68
+ });
81
69
  } else if (platform === 'linux') {
82
70
  // Linux: systemd
83
71
  const execStart = `/usr/bin/node ${DAEMON_PATH}`;
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "claw-subagent-service",
3
- "version": "0.0.10",
3
+ "version": "0.0.11",
4
4
  "description": "虾说静态服务",
5
5
  "main": "cli.js",
6
6
  "bin": {
7
7
  "claw-subagent-service": "cli.js"
8
8
  },
9
9
  "scripts": {
10
+ "preuninstall": "node scripts/pre-uninstall.js",
10
11
  "install-service": "node scripts/install-silent.js",
11
12
  "uninstall-service": "node scripts/uninstall.js",
12
13
  "dev": "nodemon cli.js --run",
@@ -1,4 +1,3 @@
1
- const { Service } = require('node-windows');
2
1
  const path = require('path');
3
2
  const { exec } = require('child_process');
4
3
  const fs = require('fs');
@@ -45,48 +44,44 @@ if (platform === 'win32') {
45
44
  // 兜底超时
46
45
  setTimeout(() => finish(1, '安装操作超时'), 60000);
47
46
 
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();
47
+ // 先停止并删除旧服务(避免文件锁导致更新失败)
48
+ exec(`net stop "${SERVICE_NAME}" 2>nul & sc delete "${SERVICE_NAME}" 2>nul`, () => {
49
+ const binPath = `"${process.execPath}" "${DAEMON_PATH}"`;
50
+
51
+ // 使用 sc create 创建服务,避免 node-windows 在包目录生成被锁定的 wrapper
52
+ const createCmd = `sc create "${SERVICE_NAME}" binPath= "${binPath}" start= auto displayname= "Node.js 静默后台服务"`;
53
+ exec(createCmd, (err) => {
54
+ if (err) {
55
+ log(`创建服务失败: ${err.message}`);
56
+ return finish(1, `创建服务失败: ${err.message}`);
57
+ }
58
+ log('服务注册成功');
59
+
60
+ // 设置恢复策略:崩溃后自动重启
61
+ const recoveryCmd = `sc failure "${SERVICE_NAME}" reset= 0 actions= restart/0/restart/0/restart/0`;
62
+ exec(recoveryCmd, (err) => {
63
+ if (err) log(`设置恢复策略失败: ${err.message}`);
64
+ else log('恢复策略已设置:服务崩溃后系统自动无限重启');
65
+ });
60
66
 
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
- });
67
+ // 设置自动启动
68
+ exec(`sc config "${SERVICE_NAME}" start= auto`, (err) => {
69
+ if (err) log(`设置自动启动失败: ${err.message}`);
70
+ else log('启动类型已设为:自动');
71
+ });
66
72
 
67
- exec(`sc config "${SERVICE_NAME}" start= auto`, (err) => {
68
- if (err) log(`设置自动启动失败: ${err.message}`);
69
- else log('启动类型已设为:自动');
73
+ // 启动服务
74
+ exec(`net start "${SERVICE_NAME}"`, (err) => {
75
+ if (err) {
76
+ log(`启动服务失败: ${err.message}`);
77
+ finish(1, `启动服务失败: ${err.message}`);
78
+ } else {
79
+ log('服务已启动');
80
+ finish(0, '服务安装成功并已启动');
81
+ }
82
+ });
70
83
  });
71
84
  });
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
85
  });
91
86
  } else if (platform === 'linux') {
92
87
  const serviceFile = `/etc/systemd/system/${SERVICE_NAME}.service`;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * npm preuninstall 钩子
3
+ * 在卸载旧版本前停止并删除 Windows 服务,释放文件锁
4
+ */
5
+ const { execSync } = require('child_process');
6
+
7
+ const SERVICES = ['SilentNodeService', 'claw-subagent-service'];
8
+
9
+ if (process.platform === 'win32') {
10
+ for (const name of SERVICES) {
11
+ try {
12
+ execSync(`net stop "${name}" 2>nul`, { stdio: 'ignore', timeout: 10000 });
13
+ } catch (e) {}
14
+ try {
15
+ execSync(`sc delete "${name}" 2>nul`, { stdio: 'ignore', timeout: 10000 });
16
+ } catch (e) {}
17
+ }
18
+
19
+ // 等待系统释放文件句柄
20
+ try {
21
+ execSync('timeout /t 2 /nobreak >nul 2>&1', { stdio: 'ignore', timeout: 5000 });
22
+ } catch (e) {}
23
+ }
24
+
25
+ process.exit(0);
@@ -155,38 +155,23 @@ class ServiceManager {
155
155
  }
156
156
  }
157
157
 
158
- // Windows 服务安装
158
+ // Windows 服务安装(使用 sc 命令,避免 node-windows 在包目录生成被锁定的 wrapper)
159
159
  async installWindows() {
160
- // 使用 node-windows 或手动创建服务
161
- const nodeWindowsPath = path.join(__dirname, '..', '..', 'node_modules', 'node-windows');
162
-
163
- if (!fs.existsSync(nodeWindowsPath)) {
164
- this.log?.warn('[ServiceManager] node-windows 未安装,尝试安装...');
165
- await this.execCommand('npm install node-windows --save');
166
- }
167
-
168
- const Service = require('node-windows').Service;
169
- const svc = new Service({
170
- name: this.serviceName,
171
- description: this.serviceDesc,
172
- script: this.scriptPath,
173
- nodeOptions: ['--harmony', '--max_old_space_size=4096']
174
- });
175
-
176
- return new Promise((resolve, reject) => {
177
- svc.on('install', () => {
178
- this.log?.info('[ServiceManager] Windows 服务安装成功');
179
- svc.start();
180
- resolve(true);
181
- });
182
-
183
- svc.on('error', (err) => {
184
- this.log?.error(`[ServiceManager] Windows 服务安装失败: ${err.message}`);
185
- reject(err);
186
- });
187
-
188
- svc.install();
189
- });
160
+ const binPath = `"${process.execPath}" "${this.scriptPath}"`;
161
+
162
+ // 先停止并删除旧服务(避免文件锁导致更新失败)
163
+ try { await this.execCommand(`net stop "${this.serviceName}" 2>nul`); } catch (e) {}
164
+ try { await this.execCommand(`sc delete "${this.serviceName}" 2>nul`); } catch (e) {}
165
+
166
+ // 使用 sc create 创建服务
167
+ await this.execCommand(`sc create "${this.serviceName}" binPath= "${binPath}" start= auto displayname= "${this.serviceDesc}"`);
168
+ this.log?.info('[ServiceManager] Windows 服务注册成功');
169
+
170
+ // 启动服务
171
+ await this.execCommand(`net start "${this.serviceName}"`);
172
+ this.log?.info('[ServiceManager] Windows 服务已启动');
173
+
174
+ return true;
190
175
  }
191
176
 
192
177
  // Linux 服务安装 (systemd)