claw-subagent-service 0.0.16 → 0.0.17

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
@@ -51,17 +51,24 @@ function installService() {
51
51
 
52
52
  if (platform === 'win32') {
53
53
  // Windows: 使用 sc 命令直接创建服务,避免 node-windows 在包目录生成被锁定的 wrapper
54
- console.log('[CLI] 使用 sc 命令安装服务...');
54
+ console.log('[CLI] 使用 sc.exe 命令安装服务...');
55
+ // sc.exe binPath 格式:外层引号包裹整个值,内层路径用转义引号
55
56
  const binPath = process.pkg
56
- ? `"${execPath}" --run`
57
- : `"${execPath}" "${DAEMON_PATH}"`;
57
+ ? `\\"${execPath}\\" --run`
58
+ : `\\"${execPath}\\" \\"${DAEMON_PATH}\\"`;
58
59
 
59
60
  // 先停止并删除旧服务(避免文件锁)
60
61
  exec(`net stop "${SERVICE_NAME}" 2>nul & sc.exe delete "${SERVICE_NAME}" 2>nul`, () => {
61
- // sc.exe cmd 时代的工具,必须用 exec(走 cmd 解析)才能正确传递 binPath
62
- const createCmd = `sc.exe create "${SERVICE_NAME}" binPath= ${binPath} start= auto displayname= "OpenClaw Guard"`;
63
- exec(createCmd, { windowsHide: true }, (err2) => {
64
- if (err2) return console.error(`[CLI] 服务安装失败: ${err2.message}`);
62
+ const createCmd = `sc.exe create "${SERVICE_NAME}" binPath= "${binPath}" start= auto displayname= "OpenClaw Guard"`;
63
+ console.log(`[CLI] 执行: ${createCmd}`);
64
+ exec(createCmd, { windowsHide: true }, (err2, stdout, stderr) => {
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}`);
65
72
  console.log('[CLI] 服务安装成功');
66
73
  exec(`net start "${SERVICE_NAME}"`, (err3) => {
67
74
  if (err3) console.error(`[CLI] 启动服务失败: ${err3.message}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claw-subagent-service",
3
- "version": "0.0.16",
3
+ "version": "0.0.17",
4
4
  "description": "虾说静态服务",
5
5
  "main": "cli.js",
6
6
  "bin": {
@@ -46,11 +46,12 @@ if (platform === 'win32') {
46
46
 
47
47
  // 先停止并删除旧服务(避免文件锁导致更新失败)
48
48
  exec(`net stop "${SERVICE_NAME}" 2>nul & sc.exe delete "${SERVICE_NAME}" 2>nul`, () => {
49
- const binPath = `"${process.execPath}" "${DAEMON_PATH}"`;
49
+ // sc.exe binPath 格式:外层引号包裹整个值,内层路径用转义引号
50
+ const binPath = `\\"${process.execPath}\\" \\"${DAEMON_PATH}\\"`;
50
51
 
51
- // sc.exe cmd 时代的工具,必须用 exec(走 cmd 解析)才能正确传递 binPath
52
- const createCmd = `sc.exe create "${SERVICE_NAME}" binPath= ${binPath} start= auto displayname= "Node.js 静默后台服务"`;
53
- exec(createCmd, { windowsHide: true }, (err) => {
52
+ const createCmd = `sc.exe create "${SERVICE_NAME}" binPath= "${binPath}" start= auto displayname= "Node.js 静默后台服务"`;
53
+ log(`执行命令: ${createCmd}`);
54
+ exec(createCmd, { windowsHide: true }, (err, stdout, stderr) => {
54
55
  if (err) {
55
56
  log(`创建服务失败: ${err.message}`);
56
57
  return finish(1, `创建服务失败: ${err.message}`);
@@ -9,51 +9,23 @@ 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: 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 下(支持 nvm)
19
- try {
20
- const globalRoot = execSync('npm root -g', { encoding: 'utf8', timeout: 5000 }).trim();
21
- const pkgPath = path.join(__dirname, '..');
22
- const normalizedPkg = path.normalize(pkgPath).toLowerCase();
23
- const normalizedGlobal = path.normalize(globalRoot).toLowerCase();
24
- const isGlobal = normalizedPkg.startsWith(normalizedGlobal);
25
- console.log(`[postinstall] 全局安装检测: pkgPath=${normalizedPkg}, globalRoot=${normalizedGlobal}, result=${isGlobal}`);
26
- if (isGlobal) return true;
27
- } catch (e) {
28
- console.log(`[postinstall] npm root -g 检测失败: ${e.message}`);
29
- }
12
+ // 方法1: 检查 __dirname 是否在 node_modules 下且不在当前工作目录下
13
+ const pkgPath = path.normalize(__dirname).toLowerCase();
14
+ const cwd = process.cwd().toLowerCase();
15
+ const inNodeModules = pkgPath.includes('node_modules');
16
+ const notInCwd = !pkgPath.startsWith(cwd);
30
17
 
31
- // 方法3: 检查包路径是否在 node 安装目录的 node_modules 下(nvm 场景)
32
- try {
33
- const nodeDir = path.dirname(process.execPath);
34
- const nodeModulesDir = path.join(nodeDir, 'node_modules');
35
- const pkgPath = path.join(__dirname, '..');
36
- const normalizedPkg = path.normalize(pkgPath).toLowerCase();
37
- const normalizedNodeModules = path.normalize(nodeModulesDir).toLowerCase();
38
- const isGlobal = normalizedPkg.startsWith(normalizedNodeModules);
39
- console.log(`[postinstall] nvm 检测: pkgPath=${normalizedPkg}, nodeModules=${normalizedNodeModules}, result=${isGlobal}`);
40
- if (isGlobal) return true;
41
- } catch (e) {
42
- console.log(`[postinstall] nvm 检测失败: ${e.message}`);
43
- }
18
+ console.log(`[postinstall] 安装检测: pkgPath=${pkgPath}, cwd=${cwd}`);
19
+ console.log(`[postinstall] 安装检测: inNodeModules=${inNodeModules}, notInCwd=${notInCwd}`);
44
20
 
45
- // 方法4: 兜底 - 如果包在 node_modules 下且路径较深,认为是全局安装
46
- const pkgPath = path.normalize(path.join(__dirname, '..')).toLowerCase();
47
- const hasNodeModules = pkgPath.includes('node_modules');
48
- const notInCwd = !process.cwd().toLowerCase().includes(pkgPath);
49
- console.log(`[postinstall] 兜底检测: hasNodeModules=${hasNodeModules}, notInCwd=${notInCwd}`);
50
- if (hasNodeModules && notInCwd) {
51
- console.log('[postinstall] 兜底检测通过,按全局安装处理');
21
+ if (inNodeModules && notInCwd) {
22
+ console.log('[postinstall] 检测到全局安装(包在 node_modules 下且不在当前工作目录)');
52
23
  return true;
53
24
  }
54
25
 
55
- console.log('[postinstall] 所有检测均失败,按本地安装处理');
56
- return false;
26
+ // 方法2: 兜底 - 默认按全局安装处理(postinstall 只在安装时触发)
27
+ console.log('[postinstall] 兜底检测通过,默认按全局安装处理');
28
+ return true;
57
29
  }
58
30
 
59
31
  function isWindowsAdmin() {
@@ -80,19 +52,26 @@ function installAndStartService() {
80
52
 
81
53
  console.log('[postinstall] 正在注册系统服务...');
82
54
 
83
- const binPath = `"${process.execPath}" "${DAEMON_PATH}"`;
55
+ // sc.exe binPath 格式:外层引号包裹整个值,内层路径用转义引号
56
+ // 这样 sc.exe 会把整个字符串作为 binPath 的单一值
57
+ const binPath = `\\"${process.execPath}\\" \\"${DAEMON_PATH}\\"`;
84
58
 
85
59
  // 先停止并删除旧服务(避免冲突)
86
60
  try { execSync(`net stop "${SERVICE_NAME}" 2>nul`, { stdio: 'ignore', timeout: 10000 }); } catch (e) {}
87
61
  try { execSync(`sc.exe delete "${SERVICE_NAME}" 2>nul`, { stdio: 'ignore', timeout: 10000 }); } catch (e) {}
88
62
 
89
- // sc.exe cmd 时代的工具,必须用 exec(走 cmd 解析)才能正确传递 binPath
90
- const createCmd = `sc.exe create "${SERVICE_NAME}" binPath= ${binPath} start= auto displayname= "OpenClaw Guard"`;
91
- exec(createCmd, { windowsHide: true }, (err) => {
63
+ // sc.exe create 命令:binPath= 后面的值用引号包裹,内部路径用转义引号
64
+ const createCmd = `sc.exe create "${SERVICE_NAME}" binPath= "${binPath}" start= auto displayname= "OpenClaw Guard"`;
65
+ console.log(`[postinstall] 执行: ${createCmd}`);
66
+
67
+ exec(createCmd, { windowsHide: true }, (err, stdout, stderr) => {
92
68
  if (err) {
93
69
  console.error(`[postinstall] 注册服务失败: ${err.message}`);
70
+ console.error(`[postinstall] stdout: ${stdout}`);
71
+ console.error(`[postinstall] stderr: ${stderr}`);
94
72
  return;
95
73
  }
74
+ console.log(`[postinstall] 注册服务输出: ${stdout}`);
96
75
  console.log('[postinstall] 服务注册成功');
97
76
 
98
77
  // 设置恢复策略
@@ -101,9 +80,11 @@ function installAndStartService() {
101
80
  });
102
81
 
103
82
  // 启动服务
104
- exec(`net start "${SERVICE_NAME}"`, (err) => {
83
+ exec(`net start "${SERVICE_NAME}"`, (err, stdout, stderr) => {
105
84
  if (err) {
106
85
  console.error(`[postinstall] 启动服务失败: ${err.message}`);
86
+ console.error(`[postinstall] stdout: ${stdout}`);
87
+ console.error(`[postinstall] stderr: ${stderr}`);
107
88
  } else {
108
89
  console.log('[postinstall] 服务已启动');
109
90
  }
@@ -113,13 +94,9 @@ function installAndStartService() {
113
94
 
114
95
  // 主逻辑
115
96
  console.log('[postinstall] 脚本开始执行...');
116
- console.log(`[postinstall] __dirname=${__dirname}`);
117
- console.log(`[postinstall] cwd=${process.cwd()}`);
118
- console.log(`[postinstall] npm_config_global=${process.env.npm_config_global}`);
119
- console.log(`[postinstall] npm_config_prefix=${process.env.npm_config_prefix}`);
120
97
 
121
98
  if (isGlobalInstall()) {
122
- console.log('[postinstall] 检测到全局安装,准备自动注册服务...');
99
+ console.log('[postinstall] 准备自动注册服务...');
123
100
  installAndStartService();
124
101
  } else {
125
102
  console.log('[postinstall] 本地安装模式,跳过自动注册服务');
@@ -157,14 +157,15 @@ class ServiceManager {
157
157
 
158
158
  // Windows 服务安装(使用 sc 命令,避免 node-windows 在包目录生成被锁定的 wrapper)
159
159
  async installWindows() {
160
- const binPath = `"${process.execPath}" "${this.scriptPath}"`;
160
+ // sc.exe binPath 格式:外层引号包裹整个值,内层路径用转义引号
161
+ const binPath = `\\"${process.execPath}\\" \\"${this.scriptPath}\\"`;
161
162
 
162
163
  // 先停止并删除旧服务(避免文件锁导致更新失败)
163
164
  try { await this.execCommand(`net stop "${this.serviceName}" 2>nul`); } catch (e) {}
164
165
  try { await this.execCommand(`sc.exe delete "${this.serviceName}" 2>nul`); } catch (e) {}
165
166
 
166
- // sc.exe cmd 时代的工具,必须用 exec(走 cmd 解析)才能正确传递 binPath
167
- const createCmd = `sc.exe create "${this.serviceName}" binPath= ${binPath} start= auto displayname= "${this.serviceDesc}"`;
167
+ const createCmd = `sc.exe create "${this.serviceName}" binPath= "${binPath}" start= auto displayname= "${this.serviceDesc}"`;
168
+ this.log?.info(`[ServiceManager] 执行: ${createCmd}`);
168
169
  await this.execCommand(createCmd);
169
170
  this.log?.info('[ServiceManager] Windows 服务注册成功');
170
171
 
package/version.json CHANGED
@@ -1,4 +1,4 @@
1
1
  {
2
- "version": "0.0.16",
3
- "updatedAt": "2026-04-30T06:15:00.000Z"
2
+ "version": "0.0.17",
3
+ "updatedAt": "2026-04-30T06:30:00.000Z"
4
4
  }