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
|
@@ -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
|
|
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
|
-
|
|
240
|
-
|
|
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 };
|