claw-subagent-service 0.0.69 → 0.0.70

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/README.md CHANGED
@@ -549,33 +549,149 @@ npm uninstall -g claw-subagent-service
549
549
 
550
550
  ## 日志查看
551
551
 
552
- ### Windows
552
+ ### 日志文件说明
553
+
554
+ 服务运行过程中会产生以下日志文件,均位于安装目录的 `logs/` 子文件夹中:
555
+
556
+ | 日志文件 | 说明 | 关键内容 |
557
+ |----------|------|----------|
558
+ | `worker-YYYY-MM-DD.log` | Worker 进程日志 | 融云消息收发、OpenClaw SSE 流式调用、消息处理流程 |
559
+ | `daemon-YYYY-MM-DD.log` | Daemon 进程日志 | 服务启动/停止、进程监控、自动更新、端口管理 |
560
+ | `updater-YYYY-MM-DD.log` | 自动更新日志 | 版本检查、下载更新、安装结果 |
561
+
562
+ ### 日志目录位置
563
+
564
+ | 安装方式 | 日志目录路径 |
565
+ |----------|-------------|
566
+ | npm 全局安装(Linux/macOS) | `$(npm root -g)/claw-subagent-service/logs/` |
567
+ | npm 全局安装(Windows) | `%APPDATA%\npm\node_modules\claw-subagent-service\logs\` |
568
+ | 本地源码运行 | `./logs/`(项目根目录) |
569
+ | Docker 容器内 | `/usr/lib/node_modules/claw-subagent-service/logs/` 或 `/data/node_cli/logs/` |
570
+
571
+ ### Linux / macOS 查看命令
572
+
573
+ ```bash
574
+ # 1. 确定安装目录
575
+ INSTALL_DIR=$(npm root -g)/claw-subagent-service
576
+ # 如果是本地源码运行,替换为实际路径,如:
577
+ # INSTALL_DIR=/data/node_cli
578
+
579
+ # 2. 查看当天 worker 日志(实时跟踪,调试用)
580
+ tail -f $INSTALL_DIR/logs/worker-$(date +%Y-%m-%d).log
581
+
582
+ # 3. 查看 worker 日志最后 200 行
583
+ tail -n 200 $INSTALL_DIR/logs/worker-$(date +%Y-%m-%d).log
584
+
585
+ # 4. 查看 daemon 日志
586
+ tail -n 100 $INSTALL_DIR/logs/daemon-$(date +%Y-%m-%d).log
587
+
588
+ # 5. 查看 updater 日志
589
+ tail -n 50 $INSTALL_DIR/logs/updater-$(date +%Y-%m-%d).log
590
+
591
+ # 6. 列出所有日志文件及大小
592
+ ls -lah $INSTALL_DIR/logs/
593
+
594
+ # 7. 搜索包含特定关键词的日志(如错误、SSE、融云)
595
+ grep -i "error\|sse\|融云\|rongcloud\|claw" $INSTALL_DIR/logs/worker-$(date +%Y-%m-%d).log
596
+
597
+ # 8. 搜索今天的所有 ERROR 级别日志
598
+ grep "$(date +%Y-%m-%d)" $INSTALL_DIR/logs/worker-$(date +%Y-%m-%d).log | grep "\[ERROR\]"
599
+
600
+ # 9. 合并 worker + daemon 日志并按时间排序(完整时间线)
601
+ cat $INSTALL_DIR/logs/worker-$(date +%Y-%m-%d).log $INSTALL_DIR/logs/daemon-$(date +%Y-%m-%d).log | sort
602
+
603
+ # 10. 实时查看所有组件日志(使用 multitail,需安装)
604
+ # multitail $INSTALL_DIR/logs/worker-$(date +%Y-%m-%d).log $INSTALL_DIR/logs/daemon-$(date +%Y-%m-%d).log
605
+ ```
606
+
607
+ ### Windows 查看命令
553
608
 
554
609
  ```powershell
555
- # 查看当天 worker 日志(服务运行日志)
556
- Get-Content "$env:USERPROFILE\claw-subagent-service\logs\worker-$(Get-Date -Format yyyy-MM-dd).log" -Tail 50
610
+ # 1. 确定安装目录
611
+ $installDir = (npm root -g) + "\claw-subagent-service"
612
+
613
+ # 2. 查看当天 worker 日志
614
+ Get-Content "$installDir\logs\worker-$(Get-Date -Format yyyy-MM-dd).log" -Tail 100
615
+
616
+ # 3. 查看 daemon 日志
617
+ Get-Content "$installDir\logs\daemon-$(Get-Date -Format yyyy-MM-dd).log" -Tail 100
557
618
 
558
- # 查看当天 daemon 日志(守护进程日志)
559
- Get-Content "$env:USERPROFILE\claw-subagent-service\logs\daemon-$(Get-Date -Format yyyy-MM-dd).log" -Tail 50
619
+ # 4. 搜索错误关键词
620
+ Select-String -Path "$installDir\logs\*.log" -Pattern "ERROR|error|失败|异常"
560
621
 
561
- # SYSTEM 账户下运行的日志位置(服务默认以 SYSTEM 运行)
622
+ # 5. 查看 wrapper 日志(node-windows 服务生成)
623
+ Get-Content "$env:APPDATA\npm\node_modules\claw-subagent-service\service\daemon\clawsubagentservice.wrapper.log" -Tail 50
624
+
625
+ # 6. SYSTEM 账户下运行的日志(如果服务以 SYSTEM 运行)
562
626
  Get-Content "C:\Windows\System32\config\systemprofile\claw-subagent-service\logs\worker-$(Get-Date -Format yyyy-MM-dd).log" -Tail 50
627
+ ```
628
+
629
+ ### Docker 查看命令
563
630
 
564
- # wrapper 日志(node-windows 生成)
565
- Get-Content "D:\A-DM\dm-im\silent-service\service\daemon\clawsubagentservice.wrapper.log" -Tail 50
631
+ ```bash
632
+ # 1. 查看容器内日志(实时)
633
+ docker exec -it <容器名> sh -c "tail -f \$(npm root -g)/claw-subagent-service/logs/worker-\$(date +%Y-%m-%d).log"
634
+
635
+ # 2. 直接在宿主机查看容器日志文件
636
+ docker exec <容器名> cat /usr/lib/node_modules/claw-subagent-service/logs/worker-$(date +%Y-%m-%d).log
637
+
638
+ # 3. 查看容器标准输出(非日志文件,是控制台输出)
639
+ docker logs -f <容器名> --tail 200
640
+
641
+ # 4. 将容器日志复制到宿主机
642
+ docker cp <容器名>:/usr/lib/node_modules/claw-subagent-service/logs/ ./claw-logs/
566
643
  ```
567
644
 
568
- ### Linux
645
+ ### 按运行模式查看日志
646
+
647
+ #### systemd 模式(Linux 服务器)
569
648
 
570
649
  ```bash
571
- # 查看 systemd 日志
650
+ # 查看 systemd 管理的实时日志
572
651
  sudo journalctl -u claw-subagent-service -f
573
652
 
574
- # 或直接查看日志文件
575
- tail -f ~/claw-subagent-service/logs/worker-$(date +%Y-%m-%d).log
653
+ # 查看最近 100 条日志
654
+ sudo journalctl -u claw-subagent-service -n 100
655
+
656
+ # 查看今天所有日志
657
+ sudo journalctl -u claw-subagent-service --since today
658
+
659
+ # 查看指定时间段的日志
660
+ sudo journalctl -u claw-subagent-service --since "2026-05-11 06:00:00" --until "2026-05-11 07:00:00"
661
+
662
+ # 查看包含特定关键词的日志
663
+ sudo journalctl -u claw-subagent-service -g "SSE|error|融云"
664
+ ```
665
+
666
+ #### 用户级守护进程模式(无 systemd / Docker)
667
+
668
+ ```bash
669
+ # 查找日志目录(全局搜索)
670
+ find / -name "worker-*.log" -path "*/claw-subagent-service/logs/*" 2>/dev/null
671
+
672
+ # 常见路径:
673
+ # /usr/lib/node_modules/claw-subagent-service/logs/
674
+ # /usr/local/lib/node_modules/claw-subagent-service/logs/
675
+ # /root/.clawmessenger/logs/
676
+ # /data/node_cli/logs/
677
+
678
+ # 设置日志目录变量并实时查看
679
+ LOG_DIR=/usr/lib/node_modules/claw-subagent-service/logs
680
+ tail -f $LOG_DIR/worker-$(date +%Y-%m-%d).log
681
+ ```
682
+
683
+ #### 前台运行模式(调试开发)
684
+
685
+ ```bash
686
+ # 直接运行,日志输出到终端控制台
687
+ claw-subagent-service --run
688
+
689
+ # 后台运行并重定向到文件
690
+ nohup claw-subagent-service --run > /tmp/claw-subagent.log 2>&1 &
691
+ tail -f /tmp/claw-subagent.log
576
692
 
577
- # 查看 daemon 日志
578
- tail -f ~/claw-subagent-service/logs/daemon-$(date +%Y-%m-%d).log
693
+ # 使用 tee 同时输出到终端和文件
694
+ claw-subagent-service --run 2>&1 | tee /tmp/claw-subagent-$(date +%Y%m%d).log
579
695
  ```
580
696
 
581
697
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claw-subagent-service",
3
- "version": "0.0.69",
3
+ "version": "0.0.70",
4
4
  "description": "虾说智能助手",
5
5
  "main": "cli.js",
6
6
  "bin": {
@@ -14,6 +14,8 @@ class MessageHandler {
14
14
  this.nodeId = config.accountId || '';
15
15
  this.handleNormalMessage = handleNormalMessage;
16
16
  this._streamQueue = Promise.resolve();
17
+ // 存储流式消息的 RongCloud messageUID:streamId -> messageUID
18
+ this._streamMessageUIDs = new Map();
17
19
  }
18
20
 
19
21
  /**
@@ -222,8 +224,11 @@ class MessageHandler {
222
224
  // 2. 调用 OpenClaw SSE
223
225
  let hasSentChunk = false;
224
226
  try {
227
+ // 确保传入的内容是字符串(claw 类型消息 content 可能是对象)
228
+ const chatContent = typeof msg.content === 'string' ? msg.content : (msg.content?.content || JSON.stringify(msg.content));
229
+ this.log?.info(`[MessageHandler] 调用 chatStream, content_type=${typeof msg.content}, chatContent=${chatContent.substring(0, 50)}`);
225
230
  await this.openclawClient.chatStream(
226
- msg.content,
231
+ chatContent,
227
232
  msg.senderUserId,
228
233
  async (delta) => {
229
234
  buffer += delta;
@@ -253,11 +258,17 @@ class MessageHandler {
253
258
  }
254
259
 
255
260
  this.log?.info(`[MessageHandler] 流式消息完成,streamId=${streamId}, 总长度: ${fullText.length}`);
261
+
262
+ // 清理已存储的 messageUID,防止内存泄漏
263
+ this._streamMessageUIDs.delete(streamId);
256
264
  }
257
265
  );
258
266
  } catch (err) {
259
267
  this.log?.error(`[MessageHandler] 流式处理错误: ${err.message}`);
260
268
  await this._sendStreamChunk(fromUserId, targetId, conversationType, '抱歉,AI 响应出现错误,请稍后重试。', streamId, true, true, 1);
269
+
270
+ // 错误时也要清理
271
+ this._streamMessageUIDs.delete(streamId);
261
272
  throw err;
262
273
  }
263
274
  }
@@ -289,7 +300,8 @@ class MessageHandler {
289
300
  * 发送流式消息片段(通过 Python 后端代理)
290
301
  */
291
302
  async _sendStreamChunk(fromUserId, targetId, conversationType, content, streamId, isFirstChunk, isLastChunk, seq = 1) {
292
- this.log?.info(`[MessageHandler] _sendStreamChunk ENTRY: target=${targetId}, streamId=${streamId}, seq=${seq}, first=${isFirstChunk}, last=${isLastChunk}, content_len=${content?.length || 0}`);
303
+ const contentPreview = typeof content === 'string' ? content.substring(0, 100) : JSON.stringify(content).substring(0, 100);
304
+ this.log?.info(`[MessageHandler] _sendStreamChunk ENTRY: target=${targetId}, streamId=${streamId}, seq=${seq}, first=${isFirstChunk}, last=${isLastChunk}, content_len=${content?.length || 0}, content_preview=${contentPreview}`);
293
305
  if (!this.isStreamingEnabled) {
294
306
  this.log?.warn('[MessageHandler] _sendStreamChunk skipped: isStreamingEnabled=false');
295
307
  return;
@@ -298,25 +310,39 @@ class MessageHandler {
298
310
  // 使用队列确保流式消息片段串行发送,避免并发导致后端处理错乱
299
311
  this._streamQueue = this._streamQueue.then(async () => {
300
312
  try {
313
+ // 获取已存储的 RongCloud messageUID(首流响应返回的)
314
+ const messageUID = this._streamMessageUIDs.get(streamId);
315
+
316
+ const payload = {
317
+ fromUserId,
318
+ targetId,
319
+ content,
320
+ streamId,
321
+ isFirstChunk,
322
+ isLastChunk,
323
+ conversationType,
324
+ seq,
325
+ messageUID
326
+ };
327
+ this.log?.info(`[MessageHandler] _sendStreamChunk 请求体: ${JSON.stringify(payload).substring(0, 300)}`);
301
328
  const resp = await axios.post(
302
329
  `${this.config.apiBaseUrl}/im/api/proxy/stream/publish`,
303
- {
304
- fromUserId,
305
- targetId,
306
- content,
307
- streamId,
308
- isFirstChunk,
309
- isLastChunk,
310
- conversationType,
311
- seq
312
- },
330
+ payload,
313
331
  { timeout: 10000 }
314
332
  );
315
- this.log?.info(`[MessageHandler] _sendStreamChunk 成功: status=${resp.status}, seq=${seq}`);
333
+
334
+ // 首流时存储 RongCloud 返回的 messageUID
335
+ if (isFirstChunk && resp.data?.messageUID) {
336
+ this._streamMessageUIDs.set(streamId, resp.data.messageUID);
337
+ this.log?.info(`[MessageHandler] 首流 messageUID 已存储: ${resp.data.messageUID}, streamId=${streamId}`);
338
+ }
339
+
340
+ this.log?.info(`[MessageHandler] _sendStreamChunk 成功: status=${resp.status}, seq=${seq}, response=${JSON.stringify(resp.data).substring(0, 200)}`);
316
341
  } catch (err) {
317
342
  const url = `${this.config.apiBaseUrl}/im/api/proxy/stream/publish`;
318
343
  const status = err.response?.status;
319
- this.log?.warn(`[MessageHandler] 发送流式消息失败: ${err.message}, url=${url}, status=${status || 'N/A'}, seq=${seq}`);
344
+ const responseData = err.response?.data ? JSON.stringify(err.response.data).substring(0, 200) : 'N/A';
345
+ this.log?.warn(`[MessageHandler] 发送流式消息失败: ${err.message}, url=${url}, status=${status || 'N/A'}, response=${responseData}, seq=${seq}`);
320
346
  }
321
347
  });
322
348