claw-subagent-service 0.0.124 → 0.0.125
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
|
@@ -224,9 +224,9 @@ stop_docker() {
|
|
|
224
224
|
kill -15 "$p" 2>/dev/null || log_warn "kill -15 $p 失败"
|
|
225
225
|
done
|
|
226
226
|
|
|
227
|
-
# 等待
|
|
228
|
-
log_info "等待
|
|
229
|
-
sleep
|
|
227
|
+
# 等待 5 秒让服务优雅退出
|
|
228
|
+
log_info "等待 5 秒让服务优雅退出..."
|
|
229
|
+
sleep 5
|
|
230
230
|
|
|
231
231
|
# 检查是否已停止
|
|
232
232
|
local pid_after_graceful
|
|
@@ -243,7 +243,20 @@ stop_docker() {
|
|
|
243
243
|
|
|
244
244
|
# 强制停止模式(直接发送 SIGKILL)
|
|
245
245
|
log_info "正在强制停止 OpenClaw 服务(SIGKILL)..."
|
|
246
|
-
|
|
246
|
+
|
|
247
|
+
# 获取最新的进程列表(因为优雅停止后可能有新进程)
|
|
248
|
+
local current_pids=""
|
|
249
|
+
if command -v pgrep &>/dev/null; then
|
|
250
|
+
current_pids=$(pgrep -f "openclaw" | tr '\n' ' ')
|
|
251
|
+
elif command -v pidof &>/dev/null; then
|
|
252
|
+
current_pids=$(pidof openclaw)
|
|
253
|
+
else
|
|
254
|
+
current_pids=$(ps aux | grep -v grep | grep "openclaw" | awk '{print $2}' | tr '\n' ' ')
|
|
255
|
+
fi
|
|
256
|
+
|
|
257
|
+
log_info "当前 openclaw 进程: $current_pids"
|
|
258
|
+
|
|
259
|
+
for p in $current_pids; do
|
|
247
260
|
log_info "执行: kill -9 $p"
|
|
248
261
|
kill -9 "$p" 2>/dev/null || log_warn "kill -9 $p 失败"
|
|
249
262
|
done
|
|
@@ -253,12 +266,15 @@ stop_docker() {
|
|
|
253
266
|
log_info "执行: killall -9 openclaw"
|
|
254
267
|
killall -9 openclaw 2>/dev/null || log_warn "killall 失败"
|
|
255
268
|
|
|
269
|
+
# 等待 2 秒让进程退出
|
|
270
|
+
sleep 2
|
|
271
|
+
|
|
256
272
|
# 连续监控模式:每秒检查并杀死看门狗重启的进程
|
|
257
273
|
# 这样即使看门狗立即重启,也会被再次杀死
|
|
258
|
-
log_info "进入连续监控模式(最多
|
|
274
|
+
log_info "进入连续监控模式(最多 20 秒),防止看门狗自动重启..."
|
|
259
275
|
local elapsed=0
|
|
260
276
|
local consecutive_empty=0
|
|
261
|
-
while [ $elapsed -lt
|
|
277
|
+
while [ $elapsed -lt 20 ]; do
|
|
262
278
|
sleep 1
|
|
263
279
|
|
|
264
280
|
# 检查是否还有 openclaw 进程
|
|
@@ -291,6 +307,9 @@ stop_docker() {
|
|
|
291
307
|
consecutive_empty=0
|
|
292
308
|
if [ -n "$remaining_pids" ]; then
|
|
293
309
|
log_info "第 $elapsed 秒: 发现新进程 $remaining_pids,再次 kill..."
|
|
310
|
+
for rp in $remaining_pids; do
|
|
311
|
+
kill -9 "$rp" 2>/dev/null || true
|
|
312
|
+
done
|
|
294
313
|
fi
|
|
295
314
|
if [ "$port_listening" = true ]; then
|
|
296
315
|
log_info "第 $elapsed 秒: 端口 $PORT 仍在监听,尝试 fuser 终止..."
|
|
@@ -305,6 +324,14 @@ stop_docker() {
|
|
|
305
324
|
elapsed=$((elapsed + 1))
|
|
306
325
|
done
|
|
307
326
|
|
|
327
|
+
# 最终验证前,再执行一次全面清理
|
|
328
|
+
log_info "执行最终清理..."
|
|
329
|
+
pkill -9 -f "openclaw" 2>/dev/null || true
|
|
330
|
+
killall -9 openclaw 2>/dev/null || true
|
|
331
|
+
|
|
332
|
+
# 等待 2 秒
|
|
333
|
+
sleep 2
|
|
334
|
+
|
|
308
335
|
# 最终验证
|
|
309
336
|
local remaining_pids=""
|
|
310
337
|
if command -v pgrep &>/dev/null; then
|
package/package.json
CHANGED
|
@@ -20,13 +20,14 @@ class HeartbeatManager {
|
|
|
20
20
|
|
|
21
21
|
try {
|
|
22
22
|
const mac = getMacAddress();
|
|
23
|
+
// 真实检测 openclaw 状态(通过端口检查)
|
|
23
24
|
const status = await getOpenClawStatus(this.config.openclawPort || 18789);
|
|
24
25
|
const sent = await this.messageSender.sendProtocolMessage(
|
|
25
26
|
RongyunMessageTypeEnum.HEARTBEAT,
|
|
26
27
|
{
|
|
27
28
|
mac_address: mac,
|
|
28
29
|
nickname: this.config.nodeName,
|
|
29
|
-
open_claw_status: status,
|
|
30
|
+
open_claw_status: status, // 1=运行中, 0=未运行(真实检测)
|
|
30
31
|
client_status: 1,
|
|
31
32
|
}
|
|
32
33
|
);
|
|
@@ -168,26 +168,46 @@ async function verifyCommandResult(command, result, scriptOutput = '') {
|
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
if (portStatus === 1) {
|
|
171
|
-
console.error(`[OpenClawControl] 停止失败: 端口 18789
|
|
171
|
+
console.error(`[OpenClawControl] 停止失败: 端口 18789 仍在监听。尝试直接强制终止进程...`);
|
|
172
172
|
|
|
173
|
-
//
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
173
|
+
// 直接执行强制终止命令(不通过脚本)
|
|
174
|
+
try {
|
|
175
|
+
const { execSync } = require('child_process');
|
|
176
|
+
|
|
177
|
+
// 先尝试 pkill
|
|
178
|
+
try {
|
|
179
|
+
execSync('pkill -9 -f "openclaw"', { timeout: 5000 });
|
|
180
|
+
console.log('[OpenClawControl] pkill -9 -f openclaw 执行成功');
|
|
181
|
+
} catch (e) {
|
|
182
|
+
console.log('[OpenClawControl] pkill 执行失败或没有匹配进程');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// 再尝试 killall
|
|
186
|
+
try {
|
|
187
|
+
execSync('killall -9 openclaw', { timeout: 5000 });
|
|
188
|
+
console.log('[OpenClawControl] killall -9 openclaw 执行成功');
|
|
189
|
+
} catch (e) {
|
|
190
|
+
console.log('[OpenClawControl] killall 执行失败或没有匹配进程');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// 等待进程退出
|
|
194
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
195
|
+
|
|
196
|
+
// 再次验证端口
|
|
197
|
+
const finalPortStatus = await getOpenClawStatus(18789);
|
|
198
|
+
|
|
199
|
+
if (finalPortStatus !== 1) {
|
|
200
|
+
console.log(`[OpenClawControl] 强制停止成功: 端口已关闭`);
|
|
201
|
+
return {
|
|
202
|
+
status: OpenClawServiceStatus.STOP_SUCCESS,
|
|
203
|
+
message: '服务已停止(强制停止)'
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
} catch (err) {
|
|
207
|
+
console.error(`[OpenClawControl] 强制停止异常: ${err.message}`);
|
|
188
208
|
}
|
|
189
209
|
|
|
190
|
-
console.error(`[OpenClawControl] 停止失败: 端口 18789
|
|
210
|
+
console.error(`[OpenClawControl] 停止失败: 端口 18789 仍在监听。所有停止方法均已尝试。`);
|
|
191
211
|
return {
|
|
192
212
|
status: OpenClawServiceStatus.ERROR,
|
|
193
213
|
message: '停止失败: 服务仍在运行'
|
|
@@ -276,12 +296,33 @@ async function executeCommand(command, window, sendResponse) {
|
|
|
276
296
|
if (sendResponse) {
|
|
277
297
|
let httpStatus = 'success';
|
|
278
298
|
if (result.status === OpenClawServiceStatus.ERROR) httpStatus = 'error';
|
|
299
|
+
|
|
300
|
+
// 获取操作后的真实状态
|
|
301
|
+
let realStatus = result.status;
|
|
302
|
+
if (command === OpenClawCommandEnum.STOP || command === OpenClawCommandEnum.START || command === OpenClawCommandEnum.RESTART) {
|
|
303
|
+
try {
|
|
304
|
+
const { getOpenClawStatus } = require('./port-checker');
|
|
305
|
+
const portStatus = await getOpenClawStatus(18789);
|
|
306
|
+
// 更新真实状态
|
|
307
|
+
if (command === OpenClawCommandEnum.STOP) {
|
|
308
|
+
realStatus = portStatus === 1 ? OpenClawServiceStatus.RUNNING : OpenClawServiceStatus.STOP_SUCCESS;
|
|
309
|
+
} else if (command === OpenClawCommandEnum.START) {
|
|
310
|
+
realStatus = portStatus === 1 ? OpenClawServiceStatus.START_SUCCESS : OpenClawServiceStatus.ERROR;
|
|
311
|
+
} else if (command === OpenClawCommandEnum.RESTART) {
|
|
312
|
+
realStatus = portStatus === 1 ? OpenClawServiceStatus.RESTART_SUCCESS : OpenClawServiceStatus.ERROR;
|
|
313
|
+
}
|
|
314
|
+
console.log(`[OpenClawControl] 操作后真实状态检测: portStatus=${portStatus}, realStatus=${realStatus}`);
|
|
315
|
+
} catch (e) {
|
|
316
|
+
console.error(`[OpenClawControl] 真实状态检测失败: ${e.message}`);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
279
320
|
sendResponse({
|
|
280
321
|
type: 'command_result',
|
|
281
322
|
command,
|
|
282
323
|
status: httpStatus,
|
|
283
324
|
message: result.message,
|
|
284
|
-
service_status:
|
|
325
|
+
service_status: realStatus
|
|
285
326
|
});
|
|
286
327
|
}
|
|
287
328
|
|
|
@@ -337,10 +337,23 @@ class RongyunMessageHandler {
|
|
|
337
337
|
this.logInfo(`[RongyunMessageHandler] 收到设备状态请求, from=${targetId}, requestId=${requestId}`);
|
|
338
338
|
|
|
339
339
|
try {
|
|
340
|
-
// 获取 OpenClaw
|
|
340
|
+
// 获取 OpenClaw 真实运行状态(检查端口 18789)
|
|
341
341
|
const openClawStatus = await getOpenClawStatus();
|
|
342
342
|
|
|
343
|
-
//
|
|
343
|
+
// 获取真实的版本信息(如果可能)
|
|
344
|
+
let version = 'unknown';
|
|
345
|
+
try {
|
|
346
|
+
const { execSync } = require('child_process');
|
|
347
|
+
const versionOutput = execSync('openclaw --version', { encoding: 'utf8', timeout: 5000 }).trim();
|
|
348
|
+
const match = versionOutput.match(/(\d+\.\d+\.\d+)/);
|
|
349
|
+
if (match) {
|
|
350
|
+
version = match[1];
|
|
351
|
+
}
|
|
352
|
+
} catch (e) {
|
|
353
|
+
// 忽略版本获取失败
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// 构建真实状态数据
|
|
344
357
|
// openClawStatus: 1=端口监听正常(服务可用), 0=未运行(端口未监听)
|
|
345
358
|
const statusMessage = openClawStatus === 1 ? '运行中' : '未运行';
|
|
346
359
|
|
|
@@ -348,11 +361,11 @@ class RongyunMessageHandler {
|
|
|
348
361
|
open_claw_status: openClawStatus, // 1=运行中, 0=未运行
|
|
349
362
|
status_message: statusMessage,
|
|
350
363
|
mac_address: getMacAddress(),
|
|
351
|
-
version:
|
|
364
|
+
version: version,
|
|
352
365
|
timestamp: Date.now(),
|
|
353
366
|
};
|
|
354
367
|
|
|
355
|
-
this.logInfo(`[RongyunMessageHandler]
|
|
368
|
+
this.logInfo(`[RongyunMessageHandler] 设备真实状态: openClawStatus=${openClawStatus}, version=${version}`);
|
|
356
369
|
await this.sendDeviceStatusReport(targetId, requestId, statusData);
|
|
357
370
|
} catch (e) {
|
|
358
371
|
const msg = e instanceof Error ? e.message : String(e);
|