claw-subagent-service 0.0.85 → 0.0.87

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.
@@ -30,17 +30,58 @@ is_docker() {
30
30
 
31
31
  # 获取 openclaw 进程 PID
32
32
  get_openclaw_pid() {
33
- # 只通过端口查找进程(最可靠,避免僵尸进程和误匹配)
33
+ local port=18789
34
+ local pid=""
35
+
36
+ # 按优先级尝试多种工具(适配精简 Docker 镜像)
37
+ if command -v lsof &>/dev/null; then
38
+ pid=$(lsof -i :${port} -t 2>/dev/null | head -1)
39
+ if [ -n "$pid" ]; then
40
+ echo "$pid"
41
+ return
42
+ fi
43
+ fi
44
+
45
+ if command -v fuser &>/dev/null; then
46
+ pid=$(fuser ${port}/tcp 2>/dev/null | tr -d ' ')
47
+ if [ -n "$pid" ]; then
48
+ echo "$pid"
49
+ return
50
+ fi
51
+ fi
52
+
53
+ if command -v ss &>/dev/null; then
54
+ pid=$(ss -tlnp 2>/dev/null | grep ":${port} " | head -1 | sed -n 's/.*pid=\\([0-9]*\\).*/\\1/p')
55
+ if [ -n "$pid" ]; then
56
+ echo "$pid"
57
+ return
58
+ fi
59
+ fi
60
+
34
61
  if command -v netstat &>/dev/null; then
35
- local pid
36
- pid=$(netstat -tnlp 2>/dev/null | grep ":18789 " | head -1 | awk '{print $7}' | cut -d'/' -f1)
62
+ pid=$(netstat -tnlp 2>/dev/null | grep ":${port} " | head -1 | awk '{print $7}' | cut -d'/' -f1)
37
63
  if [ -n "$pid" ]; then
38
64
  echo "$pid"
39
65
  return
40
66
  fi
41
67
  fi
42
68
 
43
- # 没有监听端口,返回空(不再使用 pgrep 避免误匹配)
69
+ # 最后尝试 /proc 文件系统(最可靠,无需外部工具)
70
+ for proc_dir in /proc/[0-9]*; do
71
+ if [ -d "$proc_dir/fd" ]; then
72
+ for fd in $proc_dir/fd/*; do
73
+ if [ -L "$fd" ]; then
74
+ local target
75
+ target=$(readlink "$fd" 2>/dev/null)
76
+ if [ -n "$target" ] && echo "$target" | grep -q ":${port}"; then
77
+ basename "$proc_dir"
78
+ return
79
+ fi
80
+ fi
81
+ done
82
+ fi
83
+ done
84
+
44
85
  echo ""
45
86
  }
46
87
 
@@ -48,12 +89,21 @@ get_openclaw_pid() {
48
89
  check_port() {
49
90
  local port=$1
50
91
  if command -v ss &>/dev/null; then
51
- ss -tln | grep -q ":$port "
92
+ ss -tln 2>/dev/null | grep -q ":$port "
93
+ return $?
52
94
  elif command -v netstat &>/dev/null; then
53
- netstat -tln | grep -q ":$port "
95
+ netstat -tln 2>/dev/null | grep -q ":$port "
96
+ return $?
97
+ elif command -v lsof &>/dev/null; then
98
+ lsof -i :$port 2>/dev/null | grep -q LISTEN
99
+ return $?
100
+ elif command -v fuser &>/dev/null; then
101
+ fuser $port/tcp 2>/dev/null | grep -q '[0-9]'
102
+ return $?
54
103
  else
55
104
  # 降级:直接检查进程
56
105
  [ -n "$(get_openclaw_pid)" ]
106
+ return $?
57
107
  fi
58
108
  }
59
109
 
@@ -30,17 +30,58 @@ is_docker() {
30
30
 
31
31
  # 获取 openclaw 进程 PID
32
32
  get_openclaw_pid() {
33
- # 只通过端口查找进程(最可靠,避免僵尸进程和误匹配)
33
+ local port=18789
34
+ local pid=""
35
+
36
+ # 按优先级尝试多种工具(适配精简 Docker 镜像)
37
+ if command -v lsof &>/dev/null; then
38
+ pid=$(lsof -i :${port} -t 2>/dev/null | head -1)
39
+ if [ -n "$pid" ]; then
40
+ echo "$pid"
41
+ return
42
+ fi
43
+ fi
44
+
45
+ if command -v fuser &>/dev/null; then
46
+ pid=$(fuser ${port}/tcp 2>/dev/null | tr -d ' ')
47
+ if [ -n "$pid" ]; then
48
+ echo "$pid"
49
+ return
50
+ fi
51
+ fi
52
+
53
+ if command -v ss &>/dev/null; then
54
+ pid=$(ss -tlnp 2>/dev/null | grep ":${port} " | head -1 | sed -n 's/.*pid=\\([0-9]*\\).*/\\1/p')
55
+ if [ -n "$pid" ]; then
56
+ echo "$pid"
57
+ return
58
+ fi
59
+ fi
60
+
34
61
  if command -v netstat &>/dev/null; then
35
- local pid
36
- pid=$(netstat -tnlp 2>/dev/null | grep ":18789 " | head -1 | awk '{print $7}' | cut -d'/' -f1)
62
+ pid=$(netstat -tnlp 2>/dev/null | grep ":${port} " | head -1 | awk '{print $7}' | cut -d'/' -f1)
37
63
  if [ -n "$pid" ]; then
38
64
  echo "$pid"
39
65
  return
40
66
  fi
41
67
  fi
42
68
 
43
- # 没有监听端口,返回空(不再使用 pgrep 避免误匹配)
69
+ # 最后尝试 /proc 文件系统(最可靠,无需外部工具)
70
+ for proc_dir in /proc/[0-9]*; do
71
+ if [ -d "$proc_dir/fd" ]; then
72
+ for fd in $proc_dir/fd/*; do
73
+ if [ -L "$fd" ]; then
74
+ local target
75
+ target=$(readlink "$fd" 2>/dev/null)
76
+ if [ -n "$target" ] && echo "$target" | grep -q ":${port}"; then
77
+ basename "$proc_dir"
78
+ return
79
+ fi
80
+ fi
81
+ done
82
+ fi
83
+ done
84
+
44
85
  echo ""
45
86
  }
46
87
 
@@ -48,12 +89,21 @@ get_openclaw_pid() {
48
89
  check_port() {
49
90
  local port=$1
50
91
  if command -v ss &>/dev/null; then
51
- ss -tln | grep -q ":$port "
92
+ ss -tln 2>/dev/null | grep -q ":$port "
93
+ return $?
52
94
  elif command -v netstat &>/dev/null; then
53
- netstat -tln | grep -q ":$port "
95
+ netstat -tln 2>/dev/null | grep -q ":$port "
96
+ return $?
97
+ elif command -v lsof &>/dev/null; then
98
+ lsof -i :$port 2>/dev/null | grep -q LISTEN
99
+ return $?
100
+ elif command -v fuser &>/dev/null; then
101
+ fuser $port/tcp 2>/dev/null | grep -q '[0-9]'
102
+ return $?
54
103
  else
55
104
  # 降级:直接检查进程
56
105
  [ -n "$(get_openclaw_pid)" ]
106
+ return $?
57
107
  fi
58
108
  }
59
109
 
@@ -44,17 +44,58 @@ is_docker() {
44
44
 
45
45
  # 获取 openclaw 进程 PID
46
46
  get_openclaw_pid() {
47
- # 只通过端口查找进程(最可靠,避免僵尸进程和误匹配)
47
+ local port=18789
48
+ local pid=""
49
+
50
+ # 按优先级尝试多种工具(适配精简 Docker 镜像)
51
+ if command -v lsof &>/dev/null; then
52
+ pid=$(lsof -i :${port} -t 2>/dev/null | head -1)
53
+ if [ -n "$pid" ]; then
54
+ echo "$pid"
55
+ return
56
+ fi
57
+ fi
58
+
59
+ if command -v fuser &>/dev/null; then
60
+ pid=$(fuser ${port}/tcp 2>/dev/null | tr -d ' ')
61
+ if [ -n "$pid" ]; then
62
+ echo "$pid"
63
+ return
64
+ fi
65
+ fi
66
+
67
+ if command -v ss &>/dev/null; then
68
+ pid=$(ss -tlnp 2>/dev/null | grep ":${port} " | head -1 | sed -n 's/.*pid=\([0-9]*\).*/\1/p')
69
+ if [ -n "$pid" ]; then
70
+ echo "$pid"
71
+ return
72
+ fi
73
+ fi
74
+
48
75
  if command -v netstat &>/dev/null; then
49
- local pid
50
- pid=$(netstat -tnlp 2>/dev/null | grep ":18789 " | head -1 | awk '{print $7}' | cut -d'/' -f1)
76
+ pid=$(netstat -tnlp 2>/dev/null | grep ":${port} " | head -1 | awk '{print $7}' | cut -d'/' -f1)
51
77
  if [ -n "$pid" ]; then
52
78
  echo "$pid"
53
79
  return
54
80
  fi
55
81
  fi
56
82
 
57
- # 没有监听端口,返回空(不再使用 pgrep 避免误匹配)
83
+ # 最后尝试 /proc 文件系统(最可靠,无需外部工具)
84
+ for proc_dir in /proc/[0-9]*; do
85
+ if [ -d "$proc_dir/fd" ]; then
86
+ for fd in $proc_dir/fd/*; do
87
+ if [ -L "$fd" ]; then
88
+ local target
89
+ target=$(readlink "$fd" 2>/dev/null)
90
+ if [ -n "$target" ] && echo "$target" | grep -q ":${port}"; then
91
+ basename "$proc_dir"
92
+ return
93
+ fi
94
+ fi
95
+ done
96
+ fi
97
+ done
98
+
58
99
  echo ""
59
100
  }
60
101
 
@@ -62,12 +103,21 @@ get_openclaw_pid() {
62
103
  check_port() {
63
104
  local port=$1
64
105
  if command -v ss &>/dev/null; then
65
- ss -tln | grep -q ":$port "
106
+ ss -tln 2>/dev/null | grep -q ":$port "
107
+ return $?
66
108
  elif command -v netstat &>/dev/null; then
67
- netstat -tln | grep -q ":$port "
109
+ netstat -tln 2>/dev/null | grep -q ":$port "
110
+ return $?
111
+ elif command -v lsof &>/dev/null; then
112
+ lsof -i :$port 2>/dev/null | grep -q LISTEN
113
+ return $?
114
+ elif command -v fuser &>/dev/null; then
115
+ fuser $port/tcp 2>/dev/null | grep -q '[0-9]'
116
+ return $?
68
117
  else
69
118
  # 降级:直接检查进程
70
119
  [ -n "$(get_openclaw_pid)" ]
120
+ return $?
71
121
  fi
72
122
  }
73
123
 
@@ -42,27 +42,79 @@ PORT="18789"
42
42
  # 检查端口是否监听
43
43
  check_port() {
44
44
  local port=$1
45
- if command -v netstat &>/dev/null; then
46
- netstat -tnlp 2>/dev/null | grep -q ":$port "
45
+ if command -v ss &>/dev/null; then
46
+ ss -tln 2>/dev/null | grep -q ":$port "
47
+ return $?
48
+ elif command -v netstat &>/dev/null; then
49
+ netstat -tln 2>/dev/null | grep -q ":$port "
50
+ return $?
51
+ elif command -v lsof &>/dev/null; then
52
+ lsof -i :$port 2>/dev/null | grep -q LISTEN
53
+ return $?
54
+ elif command -v fuser &>/dev/null; then
55
+ fuser $port/tcp 2>/dev/null | grep -q '[0-9]'
56
+ return $?
47
57
  else
48
- # 降级:直接检查进程
58
+ # 最后降级:直接检查进程
49
59
  [ -n "$(get_openclaw_pid)" ]
60
+ return $?
50
61
  fi
51
62
  }
52
63
 
53
64
  # 获取 openclaw 进程 PID
54
65
  get_openclaw_pid() {
55
- # 只通过端口查找进程(最可靠,避免僵尸进程和误匹配)
66
+ local port=18789
67
+ local pid=""
68
+
69
+ # 按优先级尝试多种工具(适配精简 Docker 镜像)
70
+ if command -v lsof &>/dev/null; then
71
+ pid=$(lsof -i :${port} -t 2>/dev/null | head -1)
72
+ if [ -n "$pid" ]; then
73
+ echo "$pid"
74
+ return
75
+ fi
76
+ fi
77
+
78
+ if command -v fuser &>/dev/null; then
79
+ pid=$(fuser ${port}/tcp 2>/dev/null | tr -d ' ')
80
+ if [ -n "$pid" ]; then
81
+ echo "$pid"
82
+ return
83
+ fi
84
+ fi
85
+
86
+ if command -v ss &>/dev/null; then
87
+ pid=$(ss -tlnp 2>/dev/null | grep ":${port} " | head -1 | sed -n 's/.*pid=\\([0-9]*\\).*/\\1/p')
88
+ if [ -n "$pid" ]; then
89
+ echo "$pid"
90
+ return
91
+ fi
92
+ fi
93
+
56
94
  if command -v netstat &>/dev/null; then
57
- local pid
58
- pid=$(netstat -tnlp 2>/dev/null | grep ":18789 " | head -1 | awk '{print $7}' | cut -d'/' -f1)
95
+ pid=$(netstat -tnlp 2>/dev/null | grep ":${port} " | head -1 | awk '{print $7}' | cut -d'/' -f1)
59
96
  if [ -n "$pid" ]; then
60
97
  echo "$pid"
61
98
  return
62
99
  fi
63
100
  fi
64
101
 
65
- # 没有监听端口,返回空(不再使用 pgrep 避免误匹配)
102
+ # 最后尝试 /proc 文件系统(最可靠,无需外部工具)
103
+ for proc_dir in /proc/[0-9]*; do
104
+ if [ -d "$proc_dir/fd" ]; then
105
+ for fd in $proc_dir/fd/*; do
106
+ if [ -L "$fd" ]; then
107
+ local target
108
+ target=$(readlink "$fd" 2>/dev/null)
109
+ if [ -n "$target" ] && echo "$target" | grep -q ":${port}"; then
110
+ basename "$proc_dir"
111
+ return
112
+ fi
113
+ fi
114
+ done
115
+ fi
116
+ done
117
+
66
118
  echo ""
67
119
  }
68
120
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claw-subagent-service",
3
- "version": "0.0.85",
3
+ "version": "0.0.87",
4
4
  "description": "虾说智能助手",
5
5
  "main": "cli.js",
6
6
  "bin": {
@@ -25,6 +25,7 @@ class RongyunMessageHandler {
25
25
  this.config = config;
26
26
  this.log = log;
27
27
  this.commandLock = false;
28
+ this.commandLockTimer = null;
28
29
  this.messageSender = null;
29
30
  }
30
31
 
@@ -126,6 +127,13 @@ class RongyunMessageHandler {
126
127
  }
127
128
 
128
129
  this.commandLock = true;
130
+ // 设置 60 秒超时保护,防止命令永久卡住导致锁无法释放
131
+ this.commandLockTimer = setTimeout(() => {
132
+ this.logWarn('[RongyunMessageHandler] 命令锁超时(60秒),自动释放');
133
+ this.commandLock = false;
134
+ this.commandLockTimer = null;
135
+ }, 60000);
136
+
129
137
  try {
130
138
  await executeCommand(command, null, async (response) => {
131
139
  // 增加短暂延迟,避免融云 SDK 在收到消息后立刻回复时消息丢失
@@ -146,6 +154,10 @@ class RongyunMessageHandler {
146
154
  }, requestId, sourceId);
147
155
  } finally {
148
156
  this.commandLock = false;
157
+ if (this.commandLockTimer) {
158
+ clearTimeout(this.commandLockTimer);
159
+ this.commandLockTimer = null;
160
+ }
149
161
  }
150
162
  }
151
163
 
@@ -92,8 +92,8 @@ class ScriptExecutor {
92
92
  const fullOutput = stdout + stderr;
93
93
 
94
94
  // 调试日志:记录脚本实际输出
95
- console.log(`[ScriptExecutor-DEBUG] ${scriptName} 原始输出:\n${fullOutput}`);
96
- console.log(`[ScriptExecutor-DEBUG] ${scriptName} 输出长度: ${fullOutput.length}`);
95
+ console.log(`[ScriptExecutor] 执行脚本: ${scriptPath}`);
96
+ console.log(`[ScriptExecutor] ${scriptName} 输出:\n${fullOutput}`);
97
97
 
98
98
  return this.parseStatus(command, fullOutput);
99
99
  } catch (e) {
@@ -285,15 +285,6 @@ class ScriptExecutor {
285
285
  }
286
286
  });
287
287
 
288
- // 错误处理
289
- child.on('error', (err) => {
290
- console.error(`[ScriptExecutor] 子进程错误: ${err.message}`);
291
- if (!killed) {
292
- killed = true;
293
- clearTimeout(timer);
294
- reject(err);
295
- }
296
- });
297
288
  });
298
289
  }
299
290
 
@@ -356,6 +347,7 @@ class ScriptExecutor {
356
347
 
357
348
  if (
358
349
  upper.includes('OPENCLAW COMMAND NOT FOUND') ||
350
+ upper.includes('OPENCLAW 命令未找到') ||
359
351
  output.includes('服务 openclaw-gateway.service 未安装') ||
360
352
  output.includes('服务 openclaw-gateway.service 不存在') ||
361
353
  upper.includes('OPENCLAW-GATEWAY.SERVICE 不存在')
@@ -39,16 +39,16 @@ class RongCloudClient {
39
39
  this.log?.info('[RongCloudClient] 初始化 SDK...');
40
40
  RongIMLib.init({ appkey: this.config.appKey });
41
41
 
42
- // 注册 system_service 自定义消息类型(与前端对齐)
42
+ // 注册 command 自定义消息类型(与前端对齐)
43
43
  try {
44
44
  if (typeof RongIMLib.registerMessageType === 'function') {
45
- this.SystemServiceMessage = RongIMLib.registerMessageType('system_service', true, false);
46
- this.log?.info('[RongCloudClient] system_service 自定义消息类型已注册');
45
+ this.SystemServiceMessage = RongIMLib.registerMessageType('command', true, false);
46
+ this.log?.info('[RongCloudClient] command 自定义消息类型已注册');
47
47
  } else {
48
48
  this.log?.warn('[RongCloudClient] SDK 不支持 registerMessageType');
49
49
  }
50
50
  } catch (err) {
51
- this.log?.warn(`[RongCloudClient] 注册 system_service 消息类型失败: ${err.message}`);
51
+ this.log?.warn(`[RongCloudClient] 注册 command 消息类型失败: ${err.message}`);
52
52
  }
53
53
 
54
54
  this.log?.info(`[RongCloudClient] SDK Events: ${JSON.stringify(Object.keys(RongIMLib.Events || {}))}`);
@@ -169,8 +169,8 @@ class RongCloudClient {
169
169
  // 自定义消息 content 可能是对象,提取文本内容并保留 mentionedInfo
170
170
  if (rawContent && typeof rawContent === 'object') {
171
171
  mentionedInfo = mentionedInfo || rawContent.mentionedInfo || null;
172
- // system_service 等结构化消息保留完整 JSON(上层需要 msg_type 等字段)
173
- if (message.messageType === 'system_service' || rawContent.msg_type) {
172
+ // command 等结构化消息保留完整 JSON(上层需要 msg_type 等字段)
173
+ if (message.messageType === 'command' || rawContent.msg_type) {
174
174
  rawContent = JSON.stringify(rawContent);
175
175
  } else {
176
176
  rawContent = rawContent.content || rawContent.text || JSON.stringify(rawContent);
@@ -274,14 +274,14 @@ class RongCloudClient {
274
274
  }
275
275
  }
276
276
 
277
- async sendCustomMessage(targetId, content, conversationType, customType = 'system_service') {
277
+ async sendCustomMessage(targetId, content, conversationType, customType = 'command') {
278
278
  if (!this.isConnected) {
279
279
  this.log?.error('[RongCloudClient] 未连接,无法发送自定义消息');
280
280
  return false;
281
281
  }
282
282
 
283
283
  if (!this.SystemServiceMessage) {
284
- this.log?.error('[RongCloudClient] system_service 消息类型未注册');
284
+ this.log?.error('[RongCloudClient] command 消息类型未注册');
285
285
  return false;
286
286
  }
287
287