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.
package/command/linux/stop.sh
CHANGED
|
@@ -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
|
-
#
|
|
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 "进入连续监控模式(最多
|
|
258
|
+
log_info "进入连续监控模式(最多 15 秒),防止看门狗自动重启..."
|
|
231
259
|
local elapsed=0
|
|
232
260
|
local consecutive_empty=0
|
|
233
|
-
while [ $elapsed -lt
|
|
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
|
-
|
|
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
|
|
249
|
-
# 连续
|
|
250
|
-
if [ $consecutive_empty -ge
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
325
|
-
|
|
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
|
|
package/command/win/stop.bat
CHANGED
|
@@ -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
|
-
)
|
|
53
|
-
|
|
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
|
@@ -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
|
-
|
|
150
|
-
|
|
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
|