claw-subagent-service 0.0.12 → 0.0.14

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
@@ -12,7 +12,7 @@
12
12
  * node cli.js --status # 查看服务状态
13
13
  */
14
14
 
15
- const { spawn, exec } = require('child_process');
15
+ const { spawn, exec, execFile } = require('child_process');
16
16
  const path = require('path');
17
17
  const fs = require('fs');
18
18
  const os = require('os');
@@ -53,15 +53,21 @@ function installService() {
53
53
  // Windows: 使用 sc 命令直接创建服务,避免 node-windows 在包目录生成被锁定的 wrapper
54
54
  console.log('[CLI] 使用 sc 命令安装服务...');
55
55
  const binPath = process.pkg
56
- ? `${execPath} --run`
57
- : `${execPath} "${DAEMON_PATH}"`;
56
+ ? `"${execPath}" --run`
57
+ : `"${execPath}" "${DAEMON_PATH}"`;
58
58
 
59
59
  // 先停止并删除旧服务(避免文件锁)
60
60
  exec(`net stop "${SERVICE_NAME}" 2>nul & sc delete "${SERVICE_NAME}" 2>nul`, () => {
61
- exec(`sc create "${SERVICE_NAME}" binPath= "${binPath}" start= auto displayname= "OpenClaw Guard"`, (err2) => {
61
+ // 使用 execFile 直接传参数数组,避免 cmd 引号解析问题(execPath 可能含空格如 C:\Program Files)
62
+ execFile('sc.exe', [
63
+ 'create', SERVICE_NAME,
64
+ 'binPath=', binPath,
65
+ 'start=', 'auto',
66
+ 'displayname=', 'OpenClaw Guard'
67
+ ], { windowsHide: true }, (err2) => {
62
68
  if (err2) return console.error(`[CLI] 服务安装失败: ${err2.message}`);
63
69
  console.log('[CLI] 服务安装成功');
64
- exec(`net start ${SERVICE_NAME}`, (err3) => {
70
+ exec(`net start "${SERVICE_NAME}"`, (err3) => {
65
71
  if (err3) console.error(`[CLI] 启动服务失败: ${err3.message}`);
66
72
  });
67
73
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claw-subagent-service",
3
- "version": "0.0.12",
3
+ "version": "0.0.14",
4
4
  "description": "虾说静态服务",
5
5
  "main": "cli.js",
6
6
  "bin": {
@@ -1,5 +1,5 @@
1
1
  const path = require('path');
2
- const { exec } = require('child_process');
2
+ const { exec, execFile } = require('child_process');
3
3
  const fs = require('fs');
4
4
 
5
5
  const ROOT = process.argv[2] || process.env.SILENT_SERVICE_DIR || path.join(__dirname, '..');
@@ -46,11 +46,15 @@ if (platform === 'win32') {
46
46
 
47
47
  // 先停止并删除旧服务(避免文件锁导致更新失败)
48
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) => {
49
+ const binPath = `"${process.execPath}" "${DAEMON_PATH}"`;
50
+
51
+ // 使用 execFile 直接传参数数组,避免 cmd 引号解析问题(execPath 可能含空格如 C:\Program Files)
52
+ execFile('sc.exe', [
53
+ 'create', SERVICE_NAME,
54
+ 'binPath=', binPath,
55
+ 'start=', 'auto',
56
+ 'displayname=', 'Node.js 静默后台服务'
57
+ ], { windowsHide: true }, (err) => {
54
58
  if (err) {
55
59
  log(`创建服务失败: ${err.message}`);
56
60
  return finish(1, `创建服务失败: ${err.message}`);
@@ -2,18 +2,33 @@
2
2
  * npm postinstall 钩子
3
3
  * 全局安装完成后自动注册并启动 Windows 服务
4
4
  */
5
- const { exec, execSync } = require('child_process');
5
+ const { exec, execFile, execSync } = require('child_process');
6
6
  const path = require('path');
7
- const fs = require('fs');
8
7
 
9
8
  const SERVICE_NAME = 'claw-subagent-service';
10
9
  const DAEMON_PATH = path.join(__dirname, '..', 'service', 'daemon.js');
11
10
 
12
11
  function isGlobalInstall() {
13
- // 判断是否为全局安装:检查是否在 npm 全局目录下
14
- const cwd = process.cwd();
15
- const globalPrefix = execSync('npm prefix -g', { encoding: 'utf8' }).trim();
16
- return cwd.includes(globalPrefix) || __dirname.includes(globalPrefix);
12
+ // 方法1: npm 全局安装时会设置此环境变量
13
+ if (process.env.npm_config_global === 'true') {
14
+ console.log('[postinstall] 通过 npm_config_global 检测到全局安装');
15
+ return true;
16
+ }
17
+
18
+ // 方法2: 检查包路径是否在全局 node_modules 下
19
+ try {
20
+ const globalPrefix = execSync('npm prefix -g', { encoding: 'utf8', timeout: 5000 }).trim();
21
+ const globalModules = path.join(globalPrefix, 'node_modules');
22
+ const pkgPath = path.join(__dirname, '..');
23
+ const normalizedPkg = path.normalize(pkgPath).toLowerCase();
24
+ const normalizedGlobal = path.normalize(globalModules).toLowerCase();
25
+ const isGlobal = normalizedPkg.startsWith(normalizedGlobal);
26
+ console.log(`[postinstall] 全局安装检测: pkgPath=${normalizedPkg}, globalModules=${normalizedGlobal}, result=${isGlobal}`);
27
+ return isGlobal;
28
+ } catch (e) {
29
+ console.log(`[postinstall] 全局安装检测失败(${e.message}),默认按全局安装处理`);
30
+ return true;
31
+ }
17
32
  }
18
33
 
19
34
  function isWindowsAdmin() {
@@ -40,14 +55,19 @@ function installAndStartService() {
40
55
 
41
56
  console.log('[postinstall] 正在注册系统服务...');
42
57
 
43
- const binPath = `${process.execPath} "${DAEMON_PATH}"`;
58
+ const binPath = `"${process.execPath}" "${DAEMON_PATH}"`;
44
59
 
45
60
  // 先停止并删除旧服务(避免冲突)
46
61
  try { execSync(`net stop "${SERVICE_NAME}" 2>nul`, { stdio: 'ignore', timeout: 10000 }); } catch (e) {}
47
62
  try { execSync(`sc delete "${SERVICE_NAME}" 2>nul`, { stdio: 'ignore', timeout: 10000 }); } catch (e) {}
48
63
 
49
- // 使用 sc create 注册服务
50
- exec(`sc create "${SERVICE_NAME}" binPath= "${binPath}" start= auto displayname= "OpenClaw Guard"`, (err) => {
64
+ // 使用 execFile 直接传参数数组,避免 cmd 引号解析问题
65
+ execFile('sc.exe', [
66
+ 'create', SERVICE_NAME,
67
+ 'binPath=', binPath,
68
+ 'start=', 'auto',
69
+ 'displayname=', 'OpenClaw Guard'
70
+ ], { windowsHide: true }, (err) => {
51
71
  if (err) {
52
72
  console.error(`[postinstall] 注册服务失败: ${err.message}`);
53
73
  return;
@@ -71,6 +91,12 @@ function installAndStartService() {
71
91
  }
72
92
 
73
93
  // 主逻辑
94
+ console.log('[postinstall] 脚本开始执行...');
95
+ console.log(`[postinstall] __dirname=${__dirname}`);
96
+ console.log(`[postinstall] cwd=${process.cwd()}`);
97
+ console.log(`[postinstall] npm_config_global=${process.env.npm_config_global}`);
98
+ console.log(`[postinstall] npm_config_prefix=${process.env.npm_config_prefix}`);
99
+
74
100
  if (isGlobalInstall()) {
75
101
  console.log('[postinstall] 检测到全局安装,准备自动注册服务...');
76
102
  installAndStartService();
@@ -2,7 +2,7 @@
2
2
  * 系统服务管理器
3
3
  * 支持 Windows/Linux/macOS 系统服务注册
4
4
  */
5
- const { spawn, exec } = require('child_process');
5
+ const { spawn, exec, execFile } = require('child_process');
6
6
  const fs = require('fs');
7
7
  const path = require('path');
8
8
 
@@ -157,14 +157,24 @@ class ServiceManager {
157
157
 
158
158
  // Windows 服务安装(使用 sc 命令,避免 node-windows 在包目录生成被锁定的 wrapper)
159
159
  async installWindows() {
160
- const binPath = `${process.execPath} "${this.scriptPath}"`;
160
+ const binPath = `"${process.execPath}" "${this.scriptPath}"`;
161
161
 
162
162
  // 先停止并删除旧服务(避免文件锁导致更新失败)
163
163
  try { await this.execCommand(`net stop "${this.serviceName}" 2>nul`); } catch (e) {}
164
164
  try { await this.execCommand(`sc delete "${this.serviceName}" 2>nul`); } catch (e) {}
165
165
 
166
- // 使用 sc create 创建服务
167
- await this.execCommand(`sc create "${this.serviceName}" binPath= "${binPath}" start= auto displayname= "${this.serviceDesc}"`);
166
+ // 使用 execFile 直接传参数数组,避免 cmd 引号解析问题(execPath 可能含空格如 C:\Program Files)
167
+ await new Promise((resolve, reject) => {
168
+ execFile('sc.exe', [
169
+ 'create', this.serviceName,
170
+ 'binPath=', binPath,
171
+ 'start=', 'auto',
172
+ 'displayname=', this.serviceDesc
173
+ ], { windowsHide: true }, (err) => {
174
+ if (err) reject(err);
175
+ else resolve();
176
+ });
177
+ });
168
178
  this.log?.info('[ServiceManager] Windows 服务注册成功');
169
179
 
170
180
  // 启动服务
package/version.json CHANGED
@@ -1,4 +1,4 @@
1
1
  {
2
- "version": "1.0.0",
3
- "updatedAt": "2026-04-20T00:00:00.000Z"
2
+ "version": "0.0.14",
3
+ "updatedAt": "2026-04-30T05:50:00.000Z"
4
4
  }