claw-subagent-service 0.0.122 → 0.0.124

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.
@@ -163,7 +163,9 @@ get_openclaw_pid() {
163
163
  }
164
164
 
165
165
  # Docker 模式:停止服务
166
+ # 参数: $1 = force (1=强制停止, 空=优雅停止)
166
167
  stop_docker() {
168
+ local force="$1"
167
169
  log_info "=== OpenClaw Docker 停止模式 ==="
168
170
  log_info "检查 OpenClaw 服务状态..."
169
171
 
@@ -213,7 +215,33 @@ stop_docker() {
213
215
 
214
216
  log_info "发现 OpenClaw 进程: $all_pids"
215
217
 
216
- # 直接发送 SIGKILL(强制停止),避免 SIGTERM 被忽略
218
+ # 如果不是强制模式,先尝试优雅停止
219
+ if [ -z "$force" ]; then
220
+ log_info "正在优雅停止 OpenClaw 服务..."
221
+ # 尝试发送 SIGTERM
222
+ for p in $all_pids; do
223
+ log_info "执行: kill -15 $p"
224
+ kill -15 "$p" 2>/dev/null || log_warn "kill -15 $p 失败"
225
+ done
226
+
227
+ # 等待 3 秒让服务优雅退出
228
+ log_info "等待 3 秒让服务优雅退出..."
229
+ sleep 3
230
+
231
+ # 检查是否已停止
232
+ local pid_after_graceful
233
+ pid_after_graceful=$(get_openclaw_pid)
234
+ if [ -z "$pid_after_graceful" ] && ! check_port "$PORT"; then
235
+ log_info "OpenClaw 服务已通过优雅停止退出。"
236
+ log_info "服务已成功停止。"
237
+ log_info "Success"
238
+ exit 0
239
+ fi
240
+
241
+ log_warn "优雅停止失败,服务仍在运行,切换到强制停止..."
242
+ fi
243
+
244
+ # 强制停止模式(直接发送 SIGKILL)
217
245
  log_info "正在强制停止 OpenClaw 服务(SIGKILL)..."
218
246
  for p in $all_pids; do
219
247
  log_info "执行: kill -9 $p"
@@ -227,10 +255,10 @@ stop_docker() {
227
255
 
228
256
  # 连续监控模式:每秒检查并杀死看门狗重启的进程
229
257
  # 这样即使看门狗立即重启,也会被再次杀死
230
- log_info "进入连续监控模式(最多 10 秒),防止看门狗自动重启..."
258
+ log_info "进入连续监控模式(最多 15 秒),防止看门狗自动重启..."
231
259
  local elapsed=0
232
260
  local consecutive_empty=0
233
- while [ $elapsed -lt 10 ]; do
261
+ while [ $elapsed -lt 15 ]; do
234
262
  sleep 1
235
263
 
236
264
  # 检查是否还有 openclaw 进程
@@ -243,11 +271,17 @@ stop_docker() {
243
271
  remaining_pids=$(ps aux | grep -v grep | grep "openclaw" | awk '{print $2}' | tr '\n' ' ')
244
272
  fi
245
273
 
246
- if [ -z "$remaining_pids" ]; then
274
+ # 同时检查端口是否仍在监听
275
+ local port_listening=false
276
+ if check_port "$PORT"; then
277
+ port_listening=true
278
+ fi
279
+
280
+ if [ -z "$remaining_pids" ] && [ "$port_listening" = false ]; then
247
281
  consecutive_empty=$((consecutive_empty + 1))
248
- log_info "第 $elapsed 秒: 无 openclaw 进程(连续 $consecutive_empty 次)"
249
- # 连续 2 秒没有进程,认为已停止
250
- if [ $consecutive_empty -ge 2 ]; then
282
+ log_info "第 $elapsed 秒: 无 openclaw 进程且端口未监听(连续 $consecutive_empty 次)"
283
+ # 连续 3 秒没有进程且端口未监听,认为已停止
284
+ if [ $consecutive_empty -ge 3 ]; then
251
285
  log_info "OpenClaw 服务已停止。(看门狗已放弃重启)"
252
286
  log_info "服务已成功停止。"
253
287
  log_info "Success"
@@ -255,7 +289,15 @@ stop_docker() {
255
289
  fi
256
290
  else
257
291
  consecutive_empty=0
258
- log_info "第 $elapsed 秒: 发现新进程 $remaining_pids,再次 kill..."
292
+ if [ -n "$remaining_pids" ]; then
293
+ log_info "第 $elapsed 秒: 发现新进程 $remaining_pids,再次 kill..."
294
+ fi
295
+ if [ "$port_listening" = true ]; then
296
+ log_info "第 $elapsed 秒: 端口 $PORT 仍在监听,尝试 fuser 终止..."
297
+ if command -v fuser &>/dev/null; then
298
+ fuser -k "${PORT}/tcp" 2>/dev/null || true
299
+ fi
300
+ fi
259
301
  pkill -9 -f "openclaw" 2>/dev/null || true
260
302
  killall -9 openclaw 2>/dev/null || true
261
303
  fi
@@ -282,19 +324,33 @@ stop_docker() {
282
324
  ss -tlnp 2>/dev/null | grep ":${PORT} " || log_info "端口 ${PORT} 未监听"
283
325
  fi
284
326
 
285
- if [ -z "$remaining_pids" ]; then
327
+ # 最终验证:同时检查进程和端口
328
+ local port_listening_final=false
329
+ if check_port "$PORT"; then
330
+ port_listening_final=true
331
+ fi
332
+
333
+ if [ -z "$remaining_pids" ] && [ "$port_listening_final" = false ]; then
286
334
  log_info "OpenClaw 服务已停止。"
287
335
  log_info "服务已成功停止。"
288
336
  log_info "Success"
289
337
  exit 0
290
338
  else
291
- log_error "OpenClaw 服务停止失败!进程仍然存在: $remaining_pids"
339
+ if [ -n "$remaining_pids" ]; then
340
+ log_error "OpenClaw 服务停止失败!进程仍然存在: $remaining_pids"
341
+ fi
342
+ if [ "$port_listening_final" = true ]; then
343
+ log_error "OpenClaw 服务停止失败!端口 $PORT 仍在监听。"
344
+ fi
292
345
  exit 1
293
346
  fi
294
347
  }
295
348
 
296
349
  # Systemd 模式:停止服务
350
+ # 参数: $1 = force (1=强制停止, 空=优雅停止)
297
351
  stop_systemd() {
352
+ local force="$1"
353
+
298
354
  # 检查服务是否存在
299
355
  if ! systemctl --user list-unit-files "$SERVICE_NAME" &>/dev/null; then
300
356
  log_error "服务 $SERVICE_NAME 不存在。"
@@ -321,8 +377,33 @@ stop_systemd() {
321
377
 
322
378
  # 验证服务状态
323
379
  if systemctl --user is-active --quiet "$SERVICE_NAME"; then
324
- log_warn "服务可能仍在运行,请检查进程。"
325
- systemctl --user status "$SERVICE_NAME" --no-pager
380
+ if [ -n "$force" ]; then
381
+ log_warn "服务仍在运行,执行强制停止..."
382
+ # 获取进程 PID 并强制终止
383
+ local main_pid
384
+ main_pid=$(systemctl --user show "$SERVICE_NAME" --property=MainPID --value)
385
+ if [ -n "$main_pid" ] && [ "$main_pid" != "0" ]; then
386
+ log_info "强制终止进程 PID: $main_pid"
387
+ kill -9 "$main_pid" 2>/dev/null || true
388
+ fi
389
+ # 同时清理所有 openclaw 相关进程
390
+ pkill -9 -f "openclaw" 2>/dev/null || true
391
+ killall -9 openclaw 2>/dev/null || true
392
+
393
+ sleep 2
394
+
395
+ if ! systemctl --user is-active --quiet "$SERVICE_NAME"; then
396
+ log_info "服务已成功强制停止。"
397
+ log_info "Success"
398
+ exit 0
399
+ else
400
+ log_error "强制停止失败!服务仍在运行。"
401
+ exit 1
402
+ fi
403
+ else
404
+ log_warn "服务可能仍在运行,请检查进程。"
405
+ systemctl --user status "$SERVICE_NAME" --no-pager
406
+ fi
326
407
  else
327
408
  log_info "服务已成功停止。"
328
409
  log_info "Success"
@@ -372,9 +453,9 @@ main() {
372
453
  done
373
454
 
374
455
  if is_docker; then
375
- stop_docker
456
+ stop_docker "$force"
376
457
  else
377
- stop_systemd
458
+ stop_systemd "$force"
378
459
  fi
379
460
  }
380
461
 
@@ -43,13 +43,33 @@ echo.
43
43
  REM Wait and verify stopped
44
44
  timeout /t 3 /nobreak >nul
45
45
 
46
+ REM Check if port is still listening
46
47
  netstat -an | findstr ":18789 " | findstr "LISTENING" >nul
47
48
  if errorlevel 1 (
48
49
  echo [OK] Service stopped successfully
49
50
  echo.
50
51
  echo Success
51
52
  exit /b 0
52
- ) else (
53
- echo [WARN] Service may still be running
53
+ )
54
+
55
+ REM Port still listening, try graceful stop again with force flag
56
+ echo [WARN] Service may still be running, attempting force stop...
57
+ echo.
58
+
59
+ REM Kill openclaw processes
60
+ taskkill /f /im openclaw.exe >nul 2>&1
61
+
62
+ REM Wait for processes to terminate
63
+ timeout /t 2 /nobreak >nul
64
+
65
+ REM Final check
66
+ netstat -an | findstr ":18789 " | findstr "LISTENING" >nul
67
+ if errorlevel 1 (
68
+ echo [OK] Service stopped successfully after force stop
69
+ echo.
70
+ echo Success
54
71
  exit /b 0
72
+ ) else (
73
+ echo [ERROR] Service stop failed: port 18789 still listening after force stop
74
+ exit /b 1
55
75
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claw-subagent-service",
3
- "version": "0.0.122",
3
+ "version": "0.0.124",
4
4
  "description": "虾说智能助手",
5
5
  "main": "cli.js",
6
6
  "bin": {
@@ -138,25 +138,62 @@ async function verifyCommandResult(command, result, scriptOutput = '') {
138
138
  if (command === OpenClawCommandEnum.STOP) {
139
139
  // 停止命令验证:多次检查端口并重复执行停止,处理看门狗自动重启
140
140
  console.log(`[OpenClawControl] 开始验证停止结果...`);
141
-
141
+
142
142
  let portStatus = 1;
143
143
  let stopAttempts = 0;
144
144
  const maxStopAttempts = 3; // 最多执行 3 次停止
145
-
145
+
146
146
  // 等待 3 秒,给看门狗一次重启机会,然后验证
147
147
  await new Promise(resolve => setTimeout(resolve, 3000));
148
-
149
- portStatus = await getOpenClawStatus(18789);
150
- console.log(`[OpenClawControl] 停止后端口状态: ${portStatus}`);
151
-
148
+
149
+ while (stopAttempts < maxStopAttempts) {
150
+ portStatus = await getOpenClawStatus(18789);
151
+ console.log(`[OpenClawControl] 停止后端口状态 (尝试 ${stopAttempts + 1}/${maxStopAttempts}): ${portStatus}`);
152
+
153
+ if (portStatus !== 1) {
154
+ console.log(`[OpenClawControl] 停止验证通过: 端口已关闭`);
155
+ break;
156
+ }
157
+
158
+ stopAttempts++;
159
+ if (stopAttempts < maxStopAttempts) {
160
+ console.log(`[OpenClawControl] 端口仍在监听,尝试第 ${stopAttempts + 1} 次停止...`);
161
+ const scriptResult = await executeWithScript(command);
162
+ const retryResult = scriptResult.result;
163
+ console.log(`[OpenClawControl] 第 ${stopAttempts} 次重试停止结果: ${retryResult.status} - ${retryResult.message}`);
164
+
165
+ // 等待看门狗重启后再检查
166
+ await new Promise(resolve => setTimeout(resolve, 3000));
167
+ }
168
+ }
169
+
152
170
  if (portStatus === 1) {
171
+ console.error(`[OpenClawControl] 停止失败: 端口 18789 仍在监听。尝试强制停止...`);
172
+
173
+ // 尝试强制停止
174
+ const forceScriptResult = await executeWithScript(command);
175
+ const forceResult = forceScriptResult.result;
176
+ console.log(`[OpenClawControl] 强制停止结果: ${forceResult.status} - ${forceResult.message}`);
177
+
178
+ // 再次验证端口
179
+ await new Promise(resolve => setTimeout(resolve, 3000));
180
+ const finalPortStatus = await getOpenClawStatus(18789);
181
+
182
+ if (finalPortStatus !== 1) {
183
+ console.log(`[OpenClawControl] 强制停止成功: 端口已关闭`);
184
+ return {
185
+ status: OpenClawServiceStatus.STOP_SUCCESS,
186
+ message: '服务已停止(强制停止)'
187
+ };
188
+ }
189
+
153
190
  console.error(`[OpenClawControl] 停止失败: 端口 18789 仍在监听。stop.sh 可能未能成功停止服务。`);
154
191
  return {
155
192
  status: OpenClawServiceStatus.ERROR,
156
193
  message: '停止失败: 服务仍在运行'
157
194
  };
158
195
  }
159
-
196
+
160
197
  console.log(`[OpenClawControl] 停止验证通过: 端口已关闭`);
161
198
  } else if (command === OpenClawCommandEnum.START || command === OpenClawCommandEnum.RESTART) {
162
199
  // 等待服务启动
@@ -446,6 +446,7 @@ class ScriptExecutor {
446
446
  upper.includes('服务停止成功') ||
447
447
  upper.includes('服务已成功停止') ||
448
448
  upper.includes('GATEWAY STOP SIGNAL SENT') ||
449
+ upper.includes('STOPPED SUCCESSFULLY AFTER FORCE STOP') ||
449
450
  (upper.includes('[INFO] STOPPING SERVICE') && upper.includes('STOP SIGNAL'))
450
451
  ) {
451
452
  if (
@@ -683,8 +683,10 @@ class MessageHandler {
683
683
  // 尝试解析 content 是否为 JSON(包含 fileUrl)
684
684
  try {
685
685
  const contentObj = JSON.parse(content);
686
- if (contentObj.fileUrl) {
687
- fileUrl = contentObj.fileUrl;
686
+ if (contentObj.fileUrl || contentObj.fileUri) {
687
+ fileUrl = contentObj.fileUrl || contentObj.fileUri;
688
+ name = contentObj.name || '未知文件';
689
+ size = contentObj.size || 0;
688
690
  }
689
691
  } catch (e) {
690
692
  // content 不是 JSON