claw-subagent-service 0.0.24 → 0.0.25

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claw-subagent-service",
3
- "version": "0.0.24",
3
+ "version": "0.0.25",
4
4
  "description": "虾说静态服务",
5
5
  "main": "cli.js",
6
6
  "bin": {
@@ -115,21 +115,26 @@ class MessageHandler {
115
115
  }
116
116
 
117
117
  async handleClaw(msg) {
118
- // 先发送已读回执(表示消息已被接收和处理)
118
+ const targetId = this.getReplyTarget(msg);
119
+
120
+ // 发送已读回执(fire-and-forget,不阻塞)
119
121
  if (this.sendReadReceiptFn) {
120
- try {
121
- await this.sendReadReceiptFn(msg);
122
- this.log?.info(`[MessageHandler] 已读回执已发送`);
123
- } catch (err) {
124
- this.log?.error(`[MessageHandler] 发送已读回执失败: ${err.message}`);
125
- }
122
+ this.sendReadReceiptFn(msg).catch(() => {});
126
123
  }
127
124
 
128
- const reply = await this.openclawClient.chat(msg.content, msg.senderUserId);
129
- this.log?.info(`[MessageHandler] AI 回复: ${reply.substring(0, 50)}...`);
130
-
131
- const targetId = this.getReplyTarget(msg);
132
- await this.sendFn(targetId, reply, msg.conversationType);
125
+ // 先回复"正在处理",让用户知道消息已被接收
126
+ this.sendFn(targetId, '🤖 正在思考中,请稍候...', msg.conversationType).catch(() => {});
127
+
128
+ // 后台执行 openclaw,不阻塞消息队列
129
+ this.openclawClient.chat(msg.content, msg.senderUserId)
130
+ .then(reply => {
131
+ this.log?.info(`[MessageHandler] AI 回复: ${reply.substring(0, 50)}...`);
132
+ this.sendFn(targetId, reply, msg.conversationType).catch(() => {});
133
+ })
134
+ .catch(err => {
135
+ this.log?.error(`[MessageHandler] OpenClaw 调用失败: ${err.message}`);
136
+ this.sendFn(targetId, `❌ 处理失败: ${err.message}`, msg.conversationType).catch(() => {});
137
+ });
133
138
  }
134
139
 
135
140
  parseCommand(raw, senderId) {
@@ -1,4 +1,5 @@
1
- const { spawn } = require('child_process');
1
+ const { spawn, execSync } = require('child_process');
2
+ const net = require('net');
2
3
  const os = require('os');
3
4
  const fs = require('fs');
4
5
  const path = require('path');
@@ -31,15 +32,131 @@ function getRealHomeDir() {
31
32
  return homeDir;
32
33
  }
33
34
 
35
+ /**
36
+ * 检测端口是否监听
37
+ */
38
+ function checkPort(port) {
39
+ return new Promise((resolve) => {
40
+ const sock = new net.Socket();
41
+ sock.setTimeout(3000);
42
+ sock.once('connect', () => {
43
+ sock.destroy();
44
+ resolve(true);
45
+ });
46
+ sock.once('error', () => {
47
+ sock.destroy();
48
+ resolve(false);
49
+ });
50
+ sock.once('timeout', () => {
51
+ sock.destroy();
52
+ resolve(false);
53
+ });
54
+ sock.connect(port, '127.0.0.1');
55
+ });
56
+ }
57
+
58
+ /**
59
+ * 启动 OpenClaw gateway
60
+ */
61
+ function startOpenClawGateway(log) {
62
+ return new Promise((resolve) => {
63
+ log?.info('[OpenClawClient] 正在启动 OpenClaw gateway...');
64
+
65
+ const child = spawn('openclaw', ['gateway'], {
66
+ shell: true,
67
+ windowsHide: true,
68
+ detached: true,
69
+ stdio: 'ignore',
70
+ env: {
71
+ ...process.env,
72
+ USERPROFILE: getRealHomeDir(),
73
+ HOME: getRealHomeDir(),
74
+ },
75
+ });
76
+
77
+ child.unref();
78
+
79
+ // 等待 gateway 启动(最多 15 秒)
80
+ let attempts = 0;
81
+ const maxAttempts = 15;
82
+ const interval = setInterval(async () => {
83
+ attempts++;
84
+ const isRunning = await checkPort(18789);
85
+ if (isRunning) {
86
+ clearInterval(interval);
87
+ log?.info('[OpenClawClient] OpenClaw gateway 启动成功');
88
+ resolve(true);
89
+ } else if (attempts >= maxAttempts) {
90
+ clearInterval(interval);
91
+ log?.warn('[OpenClawClient] OpenClaw gateway 启动超时');
92
+ resolve(false);
93
+ }
94
+ }, 1000);
95
+
96
+ child.on('error', (err) => {
97
+ log?.error(`[OpenClawClient] 启动 gateway 失败: ${err.message}`);
98
+ clearInterval(interval);
99
+ resolve(false);
100
+ });
101
+ });
102
+ }
103
+
34
104
  class OpenClawClient {
35
105
  constructor(log) {
36
106
  this.log = log;
107
+ this.gatewayStarting = false;
108
+ this.gatewayStarted = false;
109
+ }
110
+
111
+ /**
112
+ * 确保 OpenClaw gateway 在运行
113
+ */
114
+ async ensureGatewayRunning() {
115
+ if (this.gatewayStarted) return true;
116
+
117
+ const isRunning = await checkPort(18789);
118
+ if (isRunning) {
119
+ this.log?.info('[OpenClawClient] OpenClaw gateway 已在运行');
120
+ this.gatewayStarted = true;
121
+ return true;
122
+ }
123
+
124
+ // 避免并发启动
125
+ if (this.gatewayStarting) {
126
+ this.log?.info('[OpenClawClient] gateway 正在启动中,等待...');
127
+ // 等待最多 20 秒
128
+ for (let i = 0; i < 20; i++) {
129
+ await new Promise(r => setTimeout(r, 1000));
130
+ if (await checkPort(18789)) {
131
+ this.gatewayStarted = true;
132
+ return true;
133
+ }
134
+ }
135
+ return false;
136
+ }
137
+
138
+ this.gatewayStarting = true;
139
+ try {
140
+ const started = await startOpenClawGateway(this.log);
141
+ this.gatewayStarted = started;
142
+ return started;
143
+ } finally {
144
+ this.gatewayStarting = false;
145
+ }
37
146
  }
38
147
 
39
148
  async chat(message, fromUser) {
40
149
  if (!message || !message.trim()) {
41
150
  return '消息内容为空';
42
151
  }
152
+
153
+ // 确保 OpenClaw gateway 已启动
154
+ const gatewayReady = await this.ensureGatewayRunning();
155
+ if (!gatewayReady) {
156
+ this.log?.error('[OpenClawClient] OpenClaw gateway 未运行且启动失败');
157
+ return 'OpenClaw gateway 启动失败,请检查 openclaw 是否正确安装';
158
+ }
159
+
43
160
  this.log?.info(`[OpenClawClient] 准备发送消息到 OpenClaw,from=${fromUser}, message=${message.substring(0, 50)}`);
44
161
 
45
162
  const sessionId = `clawmessenger-${fromUser}`;
@@ -154,13 +154,12 @@ class RongCloudClient {
154
154
  sentTime: message.sentTime || Date.now()
155
155
  };
156
156
 
157
- this.processingQueue = this.processingQueue.then(async () => {
158
- if (this.handler) {
159
- await this.handler.handleMessage(rongCloudMsg);
160
- }
161
- }).catch(err => {
162
- this.log?.error(`[RongCloudClient] 消息处理异常: ${err.message}`);
163
- });
157
+ // 并行处理消息,不等待上一条完成(避免 openclaw 长耗时调用阻塞后续消息)
158
+ if (this.handler) {
159
+ this.handler.handleMessage(rongCloudMsg).catch(err => {
160
+ this.log?.error(`[RongCloudClient] 消息处理异常: ${err.message}`);
161
+ });
162
+ }
164
163
  } catch (err) {
165
164
  this.log?.error(`[RongCloudClient] 解析消息失败: ${err.message}`);
166
165
  }