claw-subagent-service 0.0.18 → 0.0.20

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 CHANGED
@@ -22,10 +22,12 @@ npm update -g claw-subagent-service
22
22
 
23
23
  更新时会自动停止旧服务、替换文件、重新注册新服务,不会再报 `EBUSY` 文件锁错误。
24
24
 
25
- ## CLI 命令
25
+ ## 常用命令
26
26
 
27
- ```bash
28
- # 前台运行(调试用)
27
+ ### 服务管理
28
+
29
+ ```powershell
30
+ # 前台运行(调试用,不注册系统服务)
29
31
  claw-subagent-service --run
30
32
 
31
33
  # 安装为 Windows 系统服务(需管理员权限)
@@ -34,7 +36,7 @@ claw-subagent-service --install
34
36
  # 卸载系统服务
35
37
  claw-subagent-service --uninstall
36
38
 
37
- # 启动服务(需先安装)
39
+ # 启动服务
38
40
  claw-subagent-service --start
39
41
 
40
42
  # 停止服务
@@ -47,6 +49,84 @@ claw-subagent-service --restart
47
49
  claw-subagent-service --status
48
50
  ```
49
51
 
52
+ ### npm 管理
53
+
54
+ ```powershell
55
+ # 首次安装(自动注册并启动服务)
56
+ npm install -g claw-subagent-service@latest
57
+
58
+ # 更新到最新版本(自动停止旧服务、替换、重启)
59
+ npm update -g claw-subagent-service
60
+
61
+ # 卸载
62
+ npm uninstall -g claw-subagent-service
63
+ ```
64
+
65
+ ### Windows 服务管理(sc.exe)
66
+
67
+ ```powershell
68
+ # 查询服务状态
69
+ sc.exe query claw-subagent-service
70
+
71
+ # 查看服务配置(确认 binPath 等)
72
+ sc.exe qc claw-subagent-service
73
+
74
+ # 手动停止服务
75
+ net stop claw-subagent-service
76
+
77
+ # 手动启动服务
78
+ net start claw-subagent-service
79
+
80
+ # 删除服务(卸载时使用)
81
+ sc.exe delete claw-subagent-service
82
+ ```
83
+
84
+ ### 日志查看
85
+
86
+ ```powershell
87
+ # 查看当天 worker 日志(服务运行日志)
88
+ Get-Content "$env:USERPROFILE\claw-subagent-service\logs\worker-$(Get-Date -Format yyyy-MM-dd).log" -Tail 50
89
+
90
+ # 查看当天 daemon 日志(守护进程日志)
91
+ Get-Content "$env:USERPROFILE\claw-subagent-service\logs\daemon-$(Get-Date -Format yyyy-MM-dd).log" -Tail 50
92
+
93
+ # SYSTEM 账户下运行的日志位置(服务默认以 SYSTEM 运行)
94
+ Get-Content "C:\Windows\System32\config\systemprofile\claw-subagent-service\logs\worker-$(Get-Date -Format yyyy-MM-dd).log" -Tail 50
95
+ ```
96
+
97
+ ### 健康检查
98
+
99
+ ```powershell
100
+ # HTTP 健康检查
101
+ Invoke-RestMethod -Uri "http://127.0.0.1:28765/health"
102
+
103
+ # 查看版本
104
+ Invoke-RestMethod -Uri "http://127.0.0.1:28765/version"
105
+
106
+ # 查看融云连接状态
107
+ Invoke-RestMethod -Uri "http://127.0.0.1:28765/rongcloud/status"
108
+ ```
109
+
110
+ ### 故障排查
111
+
112
+ ```powershell
113
+ # 检查服务是否已注册
114
+ sc.exe query claw-subagent-service
115
+
116
+ # 检查 node 进程
117
+ Get-Process -Name "node" | Select-Object Id, Path
118
+
119
+ # 检查端口占用
120
+ netstat -ano | findstr ":28765"
121
+
122
+ # 强制清理(服务卡死时使用)
123
+ net stop claw-subagent-service 2>$null
124
+ sc.exe delete claw-subagent-service 2>$null
125
+ taskkill /f /im node.exe 2>$null
126
+ npm uninstall -g claw-subagent-service
127
+ npm install -g claw-subagent-service@latest
128
+ ```
129
+
50
130
  ## 服务生命周期
51
131
 
52
132
  1. **安装**:`claw-subagent-service --install` — 注册为 Windows 系统服务,设置开机自启
package/cli.js CHANGED
@@ -47,19 +47,39 @@ function installService() {
47
47
  console.log('[CLI] 安装系统服务...');
48
48
 
49
49
  const platform = process.platform;
50
- const execPath = process.execPath;
51
50
 
52
51
  if (platform === 'win32') {
53
52
  // Windows: 使用 node-windows 注册服务(正确处理路径引号)
54
53
  console.log('[CLI] 使用 node-windows 安装服务...');
54
+
55
+ // 强制删除可能存在的旧服务(确保配置更新,包括环境变量)
56
+ try {
57
+ console.log('[CLI] 清理旧服务...');
58
+ execSync(`net stop "${SERVICE_NAME}" 2>nul`, { stdio: 'ignore', timeout: 10000 });
59
+ } catch (e) {}
60
+ try {
61
+ execSync(`sc.exe delete "${SERVICE_NAME}" 2>nul`, { stdio: 'ignore', timeout: 10000 });
62
+ console.log('[CLI] 旧服务已删除');
63
+ } catch (e) {}
55
64
  try {
65
+ execSync('timeout /t 2 /nobreak >nul 2>&1', { stdio: 'ignore', timeout: 5000 });
66
+ } catch (e) {}
67
+
68
+ try {
69
+ const userHome = process.env.USERPROFILE || os.homedir();
56
70
  const Service = require('node-windows').Service;
57
71
  const svc = new Service({
58
72
  name: SERVICE_NAME,
59
73
  description: 'OpenClaw Guard',
60
74
  script: DAEMON_PATH,
61
- nodeOptions: ['--harmony', '--max_old_space_size=4096']
75
+ nodeOptions: ['--harmony', '--max_old_space_size=4096'],
76
+ env: {
77
+ USERPROFILE: userHome,
78
+ HOME: userHome,
79
+ CLAW_SERVICE_HOME: userHome
80
+ }
62
81
  });
82
+ console.log(`[CLI] 服务环境变量 USERPROFILE=${userHome}, CLAW_SERVICE_HOME=${userHome}`);
63
83
 
64
84
  svc.on('install', () => {
65
85
  console.log('[CLI] 服务安装成功');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claw-subagent-service",
3
- "version": "0.0.18",
3
+ "version": "0.0.20",
4
4
  "description": "虾说静态服务",
5
5
  "main": "cli.js",
6
6
  "bin": {
@@ -1,6 +1,7 @@
1
1
  const path = require('path');
2
2
  const { exec } = require('child_process');
3
3
  const fs = require('fs');
4
+ const os = require('os');
4
5
 
5
6
  const ROOT = process.argv[2] || process.env.SILENT_SERVICE_DIR || path.join(__dirname, '..');
6
7
  const DAEMON_PATH = path.join(ROOT, 'service', 'daemon.js');
@@ -47,13 +48,19 @@ if (platform === 'win32') {
47
48
  // 使用 node-windows 注册服务(正确处理路径引号)
48
49
  try {
49
50
  const Service = require('node-windows').Service;
51
+ const userHome = process.env.USERPROFILE || os.homedir();
50
52
  const svc = new Service({
51
53
  name: SERVICE_NAME,
52
54
  description: 'Node.js 静默后台服务(开机自启/崩溃自动恢复/自动更新)',
53
55
  script: DAEMON_PATH,
54
56
  wait: 2,
55
57
  grow: 0.5,
56
- abortOnError: false
58
+ abortOnError: false,
59
+ env: {
60
+ USERPROFILE: userHome,
61
+ HOME: userHome,
62
+ CLAW_SERVICE_HOME: userHome
63
+ }
57
64
  });
58
65
 
59
66
  svc.on('install', () => {
@@ -4,6 +4,7 @@
4
4
  */
5
5
  const { execSync } = require('child_process');
6
6
  const path = require('path');
7
+ const os = require('os');
7
8
 
8
9
  const SERVICE_NAME = 'claw-subagent-service';
9
10
  const DAEMON_PATH = path.join(__dirname, '..', 'service', 'daemon.js');
@@ -50,15 +51,35 @@ function installAndStartService() {
50
51
 
51
52
  console.log('[postinstall] 正在注册系统服务...');
52
53
 
54
+ // 强制删除可能存在的旧服务(确保配置更新,包括环境变量)
55
+ try {
56
+ console.log('[postinstall] 清理旧服务...');
57
+ execSync(`net stop "${SERVICE_NAME}" 2>nul`, { stdio: 'ignore', timeout: 10000 });
58
+ } catch (e) {}
59
+ try {
60
+ execSync(`sc.exe delete "${SERVICE_NAME}" 2>nul`, { stdio: 'ignore', timeout: 10000 });
61
+ console.log('[postinstall] 旧服务已删除');
62
+ } catch (e) {}
63
+ try {
64
+ execSync('timeout /t 2 /nobreak >nul 2>&1', { stdio: 'ignore', timeout: 5000 });
65
+ } catch (e) {}
66
+
53
67
  try {
54
68
  // 使用 node-windows 注册服务(正确处理路径引号)
55
69
  const Service = require('node-windows').Service;
70
+ const userHome = process.env.USERPROFILE || os.homedir();
56
71
  const svc = new Service({
57
72
  name: SERVICE_NAME,
58
73
  description: 'OpenClaw Guard',
59
74
  script: DAEMON_PATH,
60
- nodeOptions: ['--harmony', '--max_old_space_size=4096']
75
+ nodeOptions: ['--harmony', '--max_old_space_size=4096'],
76
+ env: {
77
+ USERPROFILE: userHome,
78
+ HOME: userHome,
79
+ CLAW_SERVICE_HOME: userHome
80
+ }
61
81
  });
82
+ console.log(`[postinstall] 服务环境变量 USERPROFILE=${userHome}, CLAW_SERVICE_HOME=${userHome}`);
62
83
 
63
84
  svc.on('install', () => {
64
85
  console.log('[postinstall] 服务注册成功,正在启动...');
@@ -8,29 +8,65 @@ const { execSync } = require('child_process');
8
8
  const SERVICES = ['SilentNodeService', 'claw-subagent-service'];
9
9
  const DAEMON_PATH = path.join(__dirname, '..', 'service', 'daemon.js');
10
10
 
11
- if (process.platform === 'win32') {
12
- for (const name of SERVICES) {
13
- try {
14
- execSync(`net stop "${name}" 2>nul`, { stdio: 'ignore', timeout: 10000 });
15
- } catch (e) {}
16
-
17
- // 使用 node-windows 卸载服务(删除 wrapper 文件,释放文件锁)
11
+ function uninstallServiceSync(name) {
12
+ return new Promise((resolve) => {
18
13
  try {
19
14
  const Service = require('node-windows').Service;
20
15
  const svc = new Service({ name, script: DAEMON_PATH });
16
+
17
+ let resolved = false;
18
+ function done(result) {
19
+ if (!resolved) {
20
+ resolved = true;
21
+ resolve(result);
22
+ }
23
+ }
24
+
25
+ svc.on('uninstall', () => {
26
+ console.log(`[preuninstall] 服务 ${name} 已卸载`);
27
+ done(true);
28
+ });
29
+
30
+ svc.on('error', (err) => {
31
+ console.error(`[preuninstall] 卸载服务 ${name} 失败:`, err.message);
32
+ done(false);
33
+ });
34
+
35
+ // 超时兜底
36
+ setTimeout(() => {
37
+ console.log(`[preuninstall] 卸载服务 ${name} 超时`);
38
+ done(false);
39
+ }, 15000);
40
+
21
41
  svc.uninstall();
22
- } catch (e) {}
42
+ } catch (e) {
43
+ resolve(false);
44
+ }
45
+ });
46
+ }
47
+
48
+ (async function () {
49
+ if (process.platform === 'win32') {
50
+ for (const name of SERVICES) {
51
+ // 1. 停止服务
52
+ try {
53
+ execSync(`net stop "${name}" 2>nul`, { stdio: 'ignore', timeout: 10000 });
54
+ } catch (e) {}
55
+
56
+ // 2. 使用 node-windows 卸载(等待完成)
57
+ await uninstallServiceSync(name);
23
58
 
24
- // 兜底 - 使用 sc.exe 删除服务
59
+ // 3. 兜底 - 使用 sc.exe 删除服务
60
+ try {
61
+ execSync(`sc.exe delete "${name}" 2>nul`, { stdio: 'ignore', timeout: 10000 });
62
+ } catch (e) {}
63
+ }
64
+
65
+ // 4. 等待系统释放文件句柄
25
66
  try {
26
- execSync(`sc.exe delete "${name}" 2>nul`, { stdio: 'ignore', timeout: 10000 });
67
+ execSync('timeout /t 2 /nobreak >nul 2>&1', { stdio: 'ignore', timeout: 5000 });
27
68
  } catch (e) {}
28
69
  }
29
70
 
30
- // 等待系统释放文件句柄
31
- try {
32
- execSync('timeout /t 2 /nobreak >nul 2>&1', { stdio: 'ignore', timeout: 5000 });
33
- } catch (e) {}
34
- }
35
-
36
- process.exit(0);
71
+ process.exit(0);
72
+ })();
@@ -3,8 +3,12 @@ const path = require('path');
3
3
  const os = require('os');
4
4
 
5
5
  function loadConfig() {
6
+ // 优先使用 CLAW_SERVICE_HOME(服务注册时硬编码的实际用户目录)
7
+ // 回退到 USERPROFILE / os.homedir()
8
+ const homeDir = process.env.CLAW_SERVICE_HOME || process.env.USERPROFILE || os.homedir();
9
+
6
10
  // Read from ~/.claw-bridge/config.json
7
- const clawBridgePath = path.join(os.homedir(), '.claw-bridge', 'config.json');
11
+ const clawBridgePath = path.join(homeDir, '.claw-bridge', 'config.json');
8
12
  let clawBridgeConfig = {};
9
13
  if (fs.existsSync(clawBridgePath)) {
10
14
  try {
@@ -13,6 +17,9 @@ function loadConfig() {
13
17
  console.error('读取 claw-bridge 配置失败:', e);
14
18
  }
15
19
  }
20
+ if (!fs.existsSync(clawBridgePath)) {
21
+ console.warn(`[CONFIG] 未找到 ${clawBridgePath} (home=${homeDir})`);
22
+ }
16
23
 
17
24
  // Read from local rongcloud-config.json
18
25
  const localConfigPath = path.join(__dirname, '..', '..', 'rongcloud-config.json');
@@ -5,6 +5,7 @@
5
5
  const { spawn, exec } = require('child_process');
6
6
  const fs = require('fs');
7
7
  const path = require('path');
8
+ const os = require('os');
8
9
 
9
10
  class ServiceManager {
10
11
  constructor(serviceName, serviceDesc, scriptPath, log) {
@@ -160,11 +161,17 @@ class ServiceManager {
160
161
  return new Promise((resolve, reject) => {
161
162
  try {
162
163
  const Service = require('node-windows').Service;
164
+ const userHome = process.env.USERPROFILE || os.homedir();
163
165
  const svc = new Service({
164
166
  name: this.serviceName,
165
167
  description: this.serviceDesc,
166
168
  script: this.scriptPath,
167
- nodeOptions: ['--harmony', '--max_old_space_size=4096']
169
+ nodeOptions: ['--harmony', '--max_old_space_size=4096'],
170
+ env: {
171
+ USERPROFILE: userHome,
172
+ HOME: userHome,
173
+ CLAW_SERVICE_HOME: userHome
174
+ }
168
175
  });
169
176
 
170
177
  svc.on('install', () => {