claw-subagent-service 0.0.77 → 0.0.79

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.77",
3
+ "version": "0.0.79",
4
4
  "description": "虾说智能助手",
5
5
  "main": "cli.js",
6
6
  "bin": {
@@ -11,6 +11,11 @@ const { RongyunMessageTypeEnum } = require('./rongyun-message-types');
11
11
  const { OpenClawCommandEnum } = require('./openclaw-enum');
12
12
  const { executeCommand } = require('./openclaw-control');
13
13
  const { createOpencodeSession, deleteOpencodeSession, forwardChatMessage } = require('./opencode-service');
14
+ const { ServiceManager } = require('./service-manager');
15
+ const { collectDashboardData } = require('./dashboard-collector');
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+ const os = require('os');
14
19
 
15
20
  class RongyunMessageHandler {
16
21
  constructor(rongcloudClient, config, log) {
@@ -72,6 +77,12 @@ class RongyunMessageHandler {
72
77
  case RongyunMessageTypeEnum.DELETE_OPENCODE_SESSION:
73
78
  await this.handleDeleteSession(parsed);
74
79
  break;
80
+ case RongyunMessageTypeEnum.DEVICE_CONTROL:
81
+ await this.handleDeviceControl(parsed);
82
+ break;
83
+ case RongyunMessageTypeEnum.DEVICE_STATUS_REQUEST:
84
+ await this.handleDeviceStatusRequest(parsed);
85
+ break;
75
86
  default:
76
87
  this.logWarn(`未处理的消息类型: ${msgType}`);
77
88
  }
@@ -85,8 +96,9 @@ class RongyunMessageHandler {
85
96
  const command = data.command;
86
97
  const commandId = data.command_id;
87
98
  const requestId = data.request_id;
99
+ const sourceId = data.source_im_id;
88
100
 
89
- this.logInfo(`[RongyunMessageHandler] 收到命令: command=${command}, command_id=${commandId}`);
101
+ this.logInfo(`[RongyunMessageHandler] 收到命令: command=${command}, command_id=${commandId}, from=${sourceId || 'guardserver'}`);
90
102
 
91
103
  // 验证命令是否有效
92
104
  const validCommands = Object.values(OpenClawCommandEnum);
@@ -96,7 +108,7 @@ class RongyunMessageHandler {
96
108
  command_id: commandId,
97
109
  status: 'error',
98
110
  message: `未知命令: ${command}`
99
- }, requestId);
111
+ }, requestId, sourceId);
100
112
  return;
101
113
  }
102
114
 
@@ -107,7 +119,7 @@ class RongyunMessageHandler {
107
119
  command_id: commandId,
108
120
  status: 'busy',
109
121
  message: '正在执行上一个指令,请稍后再试'
110
- }, requestId);
122
+ }, requestId, sourceId);
111
123
  return;
112
124
  }
113
125
 
@@ -119,7 +131,7 @@ class RongyunMessageHandler {
119
131
  await this.sendResponse(RongyunMessageTypeEnum.COMMAND_RESULT, {
120
132
  ...response,
121
133
  command_id: commandId
122
- }, requestId);
134
+ }, requestId, sourceId);
123
135
  });
124
136
  } catch (e) {
125
137
  const msg = e instanceof Error ? e.message : String(e);
@@ -129,7 +141,7 @@ class RongyunMessageHandler {
129
141
  command_id: commandId,
130
142
  status: 'error',
131
143
  message: msg
132
- }, requestId);
144
+ }, requestId, sourceId);
133
145
  } finally {
134
146
  this.commandLock = false;
135
147
  }
@@ -229,15 +241,125 @@ class RongyunMessageHandler {
229
241
  }
230
242
  }
231
243
 
232
- async sendResponse(msgType, content, requestId) {
244
+ async handleDeviceControl(data) {
245
+ const command = data.command;
246
+ const requestId = data.request_id;
247
+ const targetId = data.source_im_id;
248
+
249
+ this.logInfo(`[RongyunMessageHandler] 收到设备控制命令: command=${command}, from=${targetId}`);
250
+
251
+ const validCommands = ['disable', 'enable', 'delete', 'status'];
252
+ if (!validCommands.includes(command)) {
253
+ await this.sendDeviceControlResult(targetId, requestId, command, 'error', `未知命令: ${command}`);
254
+ return;
255
+ }
256
+
257
+ try {
258
+ let result;
259
+ switch (command) {
260
+ case 'disable': {
261
+ const svcMgr = new ServiceManager('claw-subagent-service', 'OpenClaw Guard CLI Client', process.argv[1], this.log);
262
+ await svcMgr.stop();
263
+ await svcMgr.uninstall();
264
+ result = { status: 'success', message: '设备服务已禁用' };
265
+ break;
266
+ }
267
+ case 'enable': {
268
+ const svcMgr = new ServiceManager('claw-subagent-service', 'OpenClaw Guard CLI Client', process.argv[1], this.log);
269
+ await svcMgr.install();
270
+ result = { status: 'success', message: '设备服务已启用' };
271
+ break;
272
+ }
273
+ case 'delete': {
274
+ const svcMgr = new ServiceManager('claw-subagent-service', 'OpenClaw Guard CLI Client', process.argv[1], this.log);
275
+ try { await svcMgr.stop(); } catch (e) {}
276
+ try { await svcMgr.uninstall(); } catch (e) {}
277
+ const homeDir = os.homedir();
278
+ const configPaths = [
279
+ path.join(homeDir, '.claw-bridge', 'config.json'),
280
+ path.join(__dirname, '..', '..', 'rongcloud-config.json')
281
+ ];
282
+ for (const p of configPaths) {
283
+ if (fs.existsSync(p)) {
284
+ fs.unlinkSync(p);
285
+ }
286
+ }
287
+ result = { status: 'success', message: '设备已删除,本地配置已清除' };
288
+ break;
289
+ }
290
+ case 'status': {
291
+ const dashboard = await collectDashboardData();
292
+ result = { status: 'success', message: '状态查询成功', data: dashboard };
293
+ break;
294
+ }
295
+ }
296
+
297
+ await this.sendDeviceControlResult(targetId, requestId, command, result.status, result.message, result.data);
298
+ } catch (e) {
299
+ const msg = e instanceof Error ? e.message : String(e);
300
+ this.logError(`设备控制命令执行异常: ${msg}`);
301
+ await this.sendDeviceControlResult(targetId, requestId, command, 'error', msg);
302
+ }
303
+ }
304
+
305
+ async handleDeviceStatusRequest(data) {
306
+ const requestId = data.request_id;
307
+ const targetId = data.source_im_id;
308
+
309
+ this.logInfo(`[RongyunMessageHandler] 收到设备状态请求, from=${targetId}`);
310
+
311
+ try {
312
+ const dashboard = await collectDashboardData();
313
+ await this.sendDeviceStatusReport(targetId, requestId, dashboard);
314
+ } catch (e) {
315
+ const msg = e instanceof Error ? e.message : String(e);
316
+ this.logError(`设备状态查询异常: ${msg}`);
317
+ await this.sendDeviceStatusReport(targetId, requestId, null, msg);
318
+ }
319
+ }
320
+
321
+ async sendDeviceControlResult(targetId, requestId, command, status, message, data) {
322
+ if (!this.messageSender) {
323
+ this.logError('MessageSender 未设置,无法发送响应');
324
+ return;
325
+ }
326
+ try {
327
+ await this.messageSender.sendDeviceControlResult(targetId, requestId, command, status, message, data);
328
+ this.logInfo(`设备控制结果已发送: ${command} -> ${targetId}`);
329
+ } catch (e) {
330
+ const msg = e instanceof Error ? e.message : String(e);
331
+ this.logError(`发送设备控制结果失败: ${msg}`);
332
+ }
333
+ }
334
+
335
+ async sendDeviceStatusReport(targetId, requestId, data, error) {
336
+ if (!this.messageSender) {
337
+ this.logError('MessageSender 未设置,无法发送响应');
338
+ return;
339
+ }
340
+ try {
341
+ await this.messageSender.sendDeviceStatusReport(targetId, requestId, data, error);
342
+ this.logInfo(`设备状态报告已发送 -> ${targetId}`);
343
+ } catch (e) {
344
+ const msg = e instanceof Error ? e.message : String(e);
345
+ this.logError(`发送设备状态报告失败: ${msg}`);
346
+ }
347
+ }
348
+
349
+ async sendResponse(msgType, content, requestId, targetId) {
233
350
  if (!this.messageSender) {
234
351
  this.logError('MessageSender 未设置,无法发送响应');
235
352
  return;
236
353
  }
237
354
 
238
355
  try {
239
- await this.messageSender.sendProtocolMessage(msgType, content, requestId);
240
- this.logInfo(`响应已发送: ${msgType}`);
356
+ if (targetId) {
357
+ await this.messageSender.sendToTarget(targetId, msgType, content, requestId);
358
+ this.logInfo(`响应已发送: ${msgType} -> ${targetId}`);
359
+ } else {
360
+ await this.messageSender.sendProtocolMessage(msgType, content, requestId);
361
+ this.logInfo(`响应已发送: ${msgType}`);
362
+ }
241
363
  } catch (e) {
242
364
  const msg = e instanceof Error ? e.message : String(e);
243
365
  this.logError(`发送响应失败: ${msg}`);
@@ -150,6 +150,75 @@ class RongyunMessageSender {
150
150
  ...data,
151
151
  });
152
152
  }
153
+
154
+ /**
155
+ * 发送消息到指定目标(P2P)
156
+ */
157
+ async sendToTarget(targetId, msgType, content, requestId) {
158
+ if (!this.rongcloudClient?.isConnected) {
159
+ this.log?.error('[RongyunMessageSender] 未连接,无法发送消息');
160
+ return false;
161
+ }
162
+
163
+ try {
164
+ const mac = getMacAddress();
165
+ const secret = generateSecret(mac, this.config.secretKey || 'secret_key');
166
+ const messagePayload = {
167
+ msg_type: msgType,
168
+ source_im_id: this.config.accountId || '',
169
+ destination_im_id: targetId,
170
+ mac: mac,
171
+ secret: secret,
172
+ content: typeof content === 'string' ? content : JSON.stringify(content),
173
+ request_id: requestId || '',
174
+ timestamp: Math.floor(Date.now() / 1000),
175
+ };
176
+
177
+ const result = await this.rongcloudClient.sendMessage(
178
+ targetId,
179
+ JSON.stringify(messagePayload),
180
+ 1 // PRIVATE
181
+ );
182
+
183
+ return result;
184
+ } catch (err) {
185
+ this.log?.error(`[RongyunMessageSender] P2P发送异常: ${err.message}`);
186
+ return false;
187
+ }
188
+ }
189
+
190
+ /**
191
+ * 发送设备控制结果(P2P)
192
+ */
193
+ async sendDeviceControlResult(targetId, requestId, command, status, message, data) {
194
+ return await this.sendToTarget(
195
+ targetId,
196
+ RongyunMessageTypeEnum.DEVICE_CONTROL_RESULT,
197
+ {
198
+ command,
199
+ status,
200
+ message,
201
+ data
202
+ },
203
+ requestId
204
+ );
205
+ }
206
+
207
+ /**
208
+ * 发送设备状态报告(P2P)
209
+ */
210
+ async sendDeviceStatusReport(targetId, requestId, data, error) {
211
+ return await this.sendToTarget(
212
+ targetId,
213
+ RongyunMessageTypeEnum.DEVICE_STATUS_REPORT,
214
+ {
215
+ status: error ? 'error' : 'success',
216
+ message: error || '状态报告',
217
+ data
218
+ },
219
+ requestId
220
+ );
221
+ }
153
222
  }
154
223
 
155
224
  module.exports = {
@@ -22,7 +22,11 @@ const RongyunMessageTypeEnum = {
22
22
  CHAT_MESSAGE: "chat_message",
23
23
  CREATE_OPENCODE_SESSION: "create_opencode_session",
24
24
  OPENCODE_SESSION_CREATED: "opencode_session_created",
25
- DELETE_OPENCODE_SESSION: "delete_opencode_session"
25
+ DELETE_OPENCODE_SESSION: "delete_opencode_session",
26
+ DEVICE_CONTROL: "device_control",
27
+ DEVICE_CONTROL_RESULT: "device_control_result",
28
+ DEVICE_STATUS_REQUEST: "device_status_request",
29
+ DEVICE_STATUS_REPORT: "device_status_report"
26
30
  };
27
31
 
28
32
  module.exports = { RongyunMessageTypeEnum };