claw-subagent-service 0.0.86 → 0.0.88

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,59 @@ 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
+ # 方法1: lsof(最可靠)
38
+ if command -v lsof &>/dev/null; then
39
+ pid=$(lsof -i :${port} -t 2>/dev/null | head -1)
40
+ if [ -n "$pid" ]; then
41
+ echo "$pid"
42
+ return
43
+ fi
44
+ fi
45
+
46
+ # 方法2: fuser
47
+ if command -v fuser &>/dev/null; then
48
+ pid=$(fuser ${port}/tcp 2>/dev/null | tr -d ' ')
49
+ if [ -n "$pid" ]; then
50
+ echo "$pid"
51
+ return
52
+ fi
53
+ fi
54
+
55
+ # 方法3: ss
56
+ if command -v ss &>/dev/null; then
57
+ pid=$(ss -tlnp 2>/dev/null | grep ":${port} " | head -1 | sed -n 's/.*pid=\([0-9]*\).*/\1/p')
58
+ if [ -n "$pid" ]; then
59
+ echo "$pid"
60
+ return
61
+ fi
62
+ fi
63
+
64
+ # 方法4: netstat
34
65
  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)
66
+ pid=$(netstat -tnlp 2>/dev/null | grep ":${port} " | head -1 | awk '{print $7}' | cut -d'/' -f1)
37
67
  if [ -n "$pid" ]; then
38
68
  echo "$pid"
39
69
  return
40
70
  fi
41
71
  fi
42
72
 
43
- # 没有监听端口,返回空(不再使用 pgrep 避免误匹配)
73
+ # 方法5: 通过 /proc/net/tcp 查找(不需要外部工具,最可靠)
74
+ # 端口 18789 的十六进制 = 0x4965
75
+ local hex_port="4965"
76
+ for proc_dir in /proc/[0-9]*; do
77
+ if [ -f "$proc_dir/net/tcp" ]; then
78
+ # 检查该进程是否监听目标端口
79
+ if grep -q "[^0-9a-fA-F]${hex_port} " "$proc_dir/net/tcp" 2>/dev/null; then
80
+ basename "$proc_dir"
81
+ return
82
+ fi
83
+ fi
84
+ done
85
+
44
86
  echo ""
45
87
  }
46
88
 
@@ -48,12 +90,21 @@ get_openclaw_pid() {
48
90
  check_port() {
49
91
  local port=$1
50
92
  if command -v ss &>/dev/null; then
51
- ss -tln | grep -q ":$port "
93
+ ss -tln 2>/dev/null | grep -q ":$port "
94
+ return $?
52
95
  elif command -v netstat &>/dev/null; then
53
- netstat -tln | grep -q ":$port "
96
+ netstat -tln 2>/dev/null | grep -q ":$port "
97
+ return $?
98
+ elif command -v lsof &>/dev/null; then
99
+ lsof -i :$port 2>/dev/null | grep -q LISTEN
100
+ return $?
101
+ elif command -v fuser &>/dev/null; then
102
+ fuser $port/tcp 2>/dev/null | grep -q '[0-9]'
103
+ return $?
54
104
  else
55
105
  # 降级:直接检查进程
56
106
  [ -n "$(get_openclaw_pid)" ]
107
+ return $?
57
108
  fi
58
109
  }
59
110
 
@@ -96,25 +147,41 @@ restart_docker() {
96
147
  fi
97
148
 
98
149
  # 如果正在运行,先停止
99
- if [ -n "$pid" ]; then
150
+ if [ -n "$pid" ] || check_port "$PORT"; then
100
151
  log_info "正在停止 OpenClaw 服务..."
101
- kill "$pid" &>/dev/null || true
152
+ if [ -n "$pid" ]; then
153
+ kill "$pid" &>/dev/null || true
154
+ fi
102
155
 
103
- # 等待停止(最多 5 秒)
156
+ # 等待停止(最多 10 秒),使用端口双重验证
104
157
  local elapsed=0
105
- while [ $elapsed -lt 5 ]; do
106
- if [ -z "$(get_openclaw_pid)" ]; then
158
+ while [ $elapsed -lt 10 ]; do
159
+ local current_pid
160
+ current_pid=$(get_openclaw_pid)
161
+ if [ -z "$current_pid" ] && ! check_port "$PORT"; then
107
162
  break
108
163
  fi
109
164
  sleep 1
110
165
  elapsed=$((elapsed + 1))
111
166
  done
112
167
 
113
- # 如果还在运行,强制停止
114
- if [ -n "$(get_openclaw_pid)" ]; then
115
- log_warn "服务未在 5 秒内停止,正在强制停止..."
116
- kill -9 "$pid" &>/dev/null || true
117
- sleep 1
168
+ # 如果还在运行,强制停止并使用备选方案
169
+ if check_port "$PORT"; then
170
+ log_warn "服务未在 10 秒内停止,正在强制停止..."
171
+ if [ -n "$pid" ]; then
172
+ kill -9 "$pid" &>/dev/null || true
173
+ fi
174
+ pkill -9 -f "openclaw" &>/dev/null || true
175
+ if command -v fuser &>/dev/null; then
176
+ fuser -k "${PORT}/tcp" &>/dev/null || true
177
+ fi
178
+ sleep 2
179
+
180
+ # 最终验证
181
+ if check_port "$PORT"; then
182
+ log_error "OpenClaw 服务停止失败!端口 $PORT 仍在监听。"
183
+ exit 1
184
+ fi
118
185
  fi
119
186
 
120
187
  log_info "服务已停止"
@@ -30,17 +30,59 @@ 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
+ # 方法1: lsof(最可靠)
38
+ if command -v lsof &>/dev/null; then
39
+ pid=$(lsof -i :${port} -t 2>/dev/null | head -1)
40
+ if [ -n "$pid" ]; then
41
+ echo "$pid"
42
+ return
43
+ fi
44
+ fi
45
+
46
+ # 方法2: fuser
47
+ if command -v fuser &>/dev/null; then
48
+ pid=$(fuser ${port}/tcp 2>/dev/null | tr -d ' ')
49
+ if [ -n "$pid" ]; then
50
+ echo "$pid"
51
+ return
52
+ fi
53
+ fi
54
+
55
+ # 方法3: ss
56
+ if command -v ss &>/dev/null; then
57
+ pid=$(ss -tlnp 2>/dev/null | grep ":${port} " | head -1 | sed -n 's/.*pid=\([0-9]*\).*/\1/p')
58
+ if [ -n "$pid" ]; then
59
+ echo "$pid"
60
+ return
61
+ fi
62
+ fi
63
+
64
+ # 方法4: netstat
34
65
  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)
66
+ pid=$(netstat -tnlp 2>/dev/null | grep ":${port} " | head -1 | awk '{print $7}' | cut -d'/' -f1)
37
67
  if [ -n "$pid" ]; then
38
68
  echo "$pid"
39
69
  return
40
70
  fi
41
71
  fi
42
72
 
43
- # 没有监听端口,返回空(不再使用 pgrep 避免误匹配)
73
+ # 方法5: 通过 /proc/net/tcp 查找(不需要外部工具,最可靠)
74
+ # 端口 18789 的十六进制 = 0x4965
75
+ local hex_port="4965"
76
+ for proc_dir in /proc/[0-9]*; do
77
+ if [ -f "$proc_dir/net/tcp" ]; then
78
+ # 检查该进程是否监听目标端口
79
+ if grep -q "[^0-9a-fA-F]${hex_port} " "$proc_dir/net/tcp" 2>/dev/null; then
80
+ basename "$proc_dir"
81
+ return
82
+ fi
83
+ fi
84
+ done
85
+
44
86
  echo ""
45
87
  }
46
88
 
@@ -48,12 +90,21 @@ get_openclaw_pid() {
48
90
  check_port() {
49
91
  local port=$1
50
92
  if command -v ss &>/dev/null; then
51
- ss -tln | grep -q ":$port "
93
+ ss -tln 2>/dev/null | grep -q ":$port "
94
+ return $?
52
95
  elif command -v netstat &>/dev/null; then
53
- netstat -tln | grep -q ":$port "
96
+ netstat -tln 2>/dev/null | grep -q ":$port "
97
+ return $?
98
+ elif command -v lsof &>/dev/null; then
99
+ lsof -i :$port 2>/dev/null | grep -q LISTEN
100
+ return $?
101
+ elif command -v fuser &>/dev/null; then
102
+ fuser $port/tcp 2>/dev/null | grep -q '[0-9]'
103
+ return $?
54
104
  else
55
105
  # 降级:直接检查进程
56
106
  [ -n "$(get_openclaw_pid)" ]
107
+ return $?
57
108
  fi
58
109
  }
59
110
 
@@ -44,17 +44,59 @@ 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
+ # 方法1: lsof(最可靠)
52
+ if command -v lsof &>/dev/null; then
53
+ pid=$(lsof -i :${port} -t 2>/dev/null | head -1)
54
+ if [ -n "$pid" ]; then
55
+ echo "$pid"
56
+ return
57
+ fi
58
+ fi
59
+
60
+ # 方法2: fuser
61
+ if command -v fuser &>/dev/null; then
62
+ pid=$(fuser ${port}/tcp 2>/dev/null | tr -d ' ')
63
+ if [ -n "$pid" ]; then
64
+ echo "$pid"
65
+ return
66
+ fi
67
+ fi
68
+
69
+ # 方法3: ss
70
+ if command -v ss &>/dev/null; then
71
+ pid=$(ss -tlnp 2>/dev/null | grep ":${port} " | head -1 | sed -n 's/.*pid=\([0-9]*\).*/\1/p')
72
+ if [ -n "$pid" ]; then
73
+ echo "$pid"
74
+ return
75
+ fi
76
+ fi
77
+
78
+ # 方法4: netstat
48
79
  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)
80
+ pid=$(netstat -tnlp 2>/dev/null | grep ":${port} " | head -1 | awk '{print $7}' | cut -d'/' -f1)
51
81
  if [ -n "$pid" ]; then
52
82
  echo "$pid"
53
83
  return
54
84
  fi
55
85
  fi
56
86
 
57
- # 没有监听端口,返回空(不再使用 pgrep 避免误匹配)
87
+ # 方法5: 通过 /proc/net/tcp 查找(不需要外部工具,最可靠)
88
+ # 端口 18789 的十六进制 = 0x4965
89
+ local hex_port="4965"
90
+ for proc_dir in /proc/[0-9]*; do
91
+ if [ -f "$proc_dir/net/tcp" ]; then
92
+ # 检查该进程是否监听目标端口
93
+ if grep -q "[^0-9a-fA-F]${hex_port} " "$proc_dir/net/tcp" 2>/dev/null; then
94
+ basename "$proc_dir"
95
+ return
96
+ fi
97
+ fi
98
+ done
99
+
58
100
  echo ""
59
101
  }
60
102
 
@@ -62,12 +104,21 @@ get_openclaw_pid() {
62
104
  check_port() {
63
105
  local port=$1
64
106
  if command -v ss &>/dev/null; then
65
- ss -tln | grep -q ":$port "
107
+ ss -tln 2>/dev/null | grep -q ":$port "
108
+ return $?
66
109
  elif command -v netstat &>/dev/null; then
67
- netstat -tln | grep -q ":$port "
110
+ netstat -tln 2>/dev/null | grep -q ":$port "
111
+ return $?
112
+ elif command -v lsof &>/dev/null; then
113
+ lsof -i :$port 2>/dev/null | grep -q LISTEN
114
+ return $?
115
+ elif command -v fuser &>/dev/null; then
116
+ fuser $port/tcp 2>/dev/null | grep -q '[0-9]'
117
+ return $?
68
118
  else
69
119
  # 降级:直接检查进程
70
120
  [ -n "$(get_openclaw_pid)" ]
121
+ return $?
71
122
  fi
72
123
  }
73
124
 
@@ -42,27 +42,80 @@ 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
+ # 方法1: lsof(最可靠)
71
+ if command -v lsof &>/dev/null; then
72
+ pid=$(lsof -i :${port} -t 2>/dev/null | head -1)
73
+ if [ -n "$pid" ]; then
74
+ echo "$pid"
75
+ return
76
+ fi
77
+ fi
78
+
79
+ # 方法2: fuser
80
+ if command -v fuser &>/dev/null; then
81
+ pid=$(fuser ${port}/tcp 2>/dev/null | tr -d ' ')
82
+ if [ -n "$pid" ]; then
83
+ echo "$pid"
84
+ return
85
+ fi
86
+ fi
87
+
88
+ # 方法3: ss
89
+ if command -v ss &>/dev/null; then
90
+ pid=$(ss -tlnp 2>/dev/null | grep ":${port} " | head -1 | sed -n 's/.*pid=\([0-9]*\).*/\1/p')
91
+ if [ -n "$pid" ]; then
92
+ echo "$pid"
93
+ return
94
+ fi
95
+ fi
96
+
97
+ # 方法4: netstat
56
98
  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)
99
+ pid=$(netstat -tnlp 2>/dev/null | grep ":${port} " | head -1 | awk '{print $7}' | cut -d'/' -f1)
59
100
  if [ -n "$pid" ]; then
60
101
  echo "$pid"
61
102
  return
62
103
  fi
63
104
  fi
64
105
 
65
- # 没有监听端口,返回空(不再使用 pgrep 避免误匹配)
106
+ # 方法5: 通过 /proc/net/tcp 查找(不需要外部工具,最可靠)
107
+ # 端口 18789 的十六进制 = 0x4965
108
+ local hex_port="4965"
109
+ for proc_dir in /proc/[0-9]*; do
110
+ if [ -f "$proc_dir/net/tcp" ]; then
111
+ # 检查该进程是否监听目标端口
112
+ if grep -q "[^0-9a-fA-F]${hex_port} " "$proc_dir/net/tcp" 2>/dev/null; then
113
+ basename "$proc_dir"
114
+ return
115
+ fi
116
+ fi
117
+ done
118
+
66
119
  echo ""
67
120
  }
68
121
 
@@ -74,6 +127,32 @@ stop_docker() {
74
127
  # 检查服务状态
75
128
  log_info "检查 OpenClaw 服务状态..."
76
129
  if [ -z "$pid" ]; then
130
+ # 即使找不到 PID,也检查端口是否还在监听
131
+ if check_port "$PORT"; then
132
+ log_warn "端口 $PORT 仍在监听,但无法获取 PID,尝试备选停止方案..."
133
+ # 尝试通过 fuser 直接通过端口杀进程
134
+ if command -v fuser &>/dev/null; then
135
+ log_info "使用 fuser 通过端口停止服务..."
136
+ fuser -k "${PORT}/tcp" &>/dev/null || true
137
+ sleep 2
138
+ fi
139
+ # 尝试通过 pkill 停止 openclaw 相关进程
140
+ if check_port "$PORT"; then
141
+ log_info "使用 pkill 停止 openclaw 进程..."
142
+ pkill -f "openclaw" &>/dev/null || true
143
+ sleep 2
144
+ fi
145
+ # 最终验证
146
+ if ! check_port "$PORT"; then
147
+ log_info "OpenClaw 服务已停止(通过备选方案)。"
148
+ log_info "服务已成功停止。"
149
+ log_info "Success"
150
+ exit 0
151
+ else
152
+ log_error "OpenClaw 服务停止失败!所有停止方案均无效。"
153
+ exit 1
154
+ fi
155
+ fi
77
156
  log_warn "OpenClaw 服务未在运行。"
78
157
  exit 0
79
158
  fi
@@ -81,14 +160,17 @@ stop_docker() {
81
160
  log_info "OpenClaw 服务正在运行(PID: $pid),准备停止..."
82
161
 
83
162
  # 停止服务:先发送 SIGTERM(优雅停止)
84
- log_info "正在停止 OpenClaw 服务..."
163
+ log_info "正在停止 OpenClaw 服务(发送 SIGTERM)..."
85
164
  kill "$pid" &>/dev/null || true
86
165
 
87
- # 等待服务完全停止(最多 10 秒)
166
+ # 等待服务完全停止(最多 10 秒),使用端口双重验证
88
167
  local elapsed=0
89
168
  while [ $elapsed -lt 10 ]; do
90
- if [ -z "$(get_openclaw_pid)" ]; then
91
- log_info "OpenClaw 服务停止成功!"
169
+ # 双重验证:检查 PID 和端口
170
+ local current_pid
171
+ current_pid=$(get_openclaw_pid)
172
+ if [ -z "$current_pid" ] && ! check_port "$PORT"; then
173
+ log_info "OpenClaw 服务停止成功!(PID 和端口均已关闭)"
92
174
  log_info "服务已成功停止。"
93
175
  log_info "Success"
94
176
  exit 0
@@ -101,32 +183,38 @@ stop_docker() {
101
183
  log_warn "服务未在 10 秒内停止,正在强制停止..."
102
184
  kill -9 "$pid" &>/dev/null || true
103
185
 
104
- # 等待进程消失(最多 3 秒)
186
+ # 额外:尝试 pkill 确保所有相关进程都被停止
187
+ pkill -9 -f "openclaw" &>/dev/null || true
188
+
189
+ # 等待进程消失(最多 5 秒)
105
190
  elapsed=0
106
- while [ $elapsed -lt 3 ]; do
107
- if [ -z "$(get_openclaw_pid)" ]; then
108
- break
191
+ while [ $elapsed -lt 5 ]; do
192
+ local current_pid
193
+ current_pid=$(get_openclaw_pid)
194
+ if [ -z "$current_pid" ] && ! check_port "$PORT"; then
195
+ log_info "OpenClaw 服务已强制停止。"
196
+ log_info "服务已成功停止。"
197
+ log_info "Success"
198
+ exit 0
109
199
  fi
110
200
  sleep 1
111
201
  elapsed=$((elapsed + 1))
112
202
  done
113
203
 
114
- # 验证:进程不存在或端口已关闭即为成功
115
- # 使用端口检测作为辅助,避免僵尸进程导致误判
204
+ # 最终验证
116
205
  local current_pid
117
206
  current_pid=$(get_openclaw_pid)
118
- if [ -z "$current_pid" ]; then
119
- log_info "OpenClaw 服务已强制停止。"
207
+ if [ -z "$current_pid" ] && ! check_port "$PORT"; then
208
+ log_info "OpenClaw 服务已停止。"
120
209
  log_info "服务已成功停止。"
121
210
  log_info "Success"
122
- elif ! check_port "$PORT"; then
123
- # 端口已关闭但进程可能还在(僵尸状态),也认为成功
211
+ elif check_port "$PORT"; then
212
+ log_error "OpenClaw 服务停止失败!端口 $PORT 仍在监听。"
213
+ exit 1
214
+ else
124
215
  log_info "OpenClaw 服务已停止(端口已关闭)。"
125
216
  log_info "服务已成功停止。"
126
217
  log_info "Success"
127
- else
128
- log_error "OpenClaw 服务停止失败!进程仍然存在且端口仍在监听。"
129
- exit 1
130
218
  fi
131
219
  }
132
220
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claw-subagent-service",
3
- "version": "0.0.86",
3
+ "version": "0.0.88",
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
 
@@ -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 不存在')