openclaw-remote-skills 1.0.2 → 1.0.3

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": "openclaw-remote-skills",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "OpenClaw remote skills package - remote-wps365-skill and remote-node-skill",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -1,612 +0,0 @@
1
- // WPS365 远程技能调用 - 核心实现
2
- // 提供远程调用 Windows 节点上 wps365-skill 的统一接口
3
-
4
- /**
5
- * WPS365 远程技能调用器
6
- */
7
- class Wps365Remote {
8
- constructor() {
9
- this.nodeId = null
10
- this.wpsSid = null
11
- this.cache = {
12
- nodeId: null,
13
- wpsSid: null,
14
- lastUpdate: 0,
15
- ttl: 300000 // 5分钟缓存
16
- }
17
- }
18
-
19
- /**
20
- * 获取 Windows 节点 ID
21
- * @returns {string} 节点ID
22
- */
23
- getWindowsNodeId() {
24
- const status = nodes(action="status")
25
- const windowsNode = status.nodes.find(node =>
26
- node.platform === "win32" && node.connected === true
27
- )
28
-
29
- if (!windowsNode) {
30
- throw new Error("未找到已连接的 Windows 节点")
31
- }
32
-
33
- return windowsNode.nodeId
34
- }
35
-
36
- /**
37
- * 读取 wps_sid 配置
38
- * @param {string} nodeId - 节点ID
39
- * @returns {string} wps_sid
40
- */
41
- async getWpsSid(nodeId) {
42
- const command = `
43
- $config = Get-Content 'C:\\Users\\KSG\\.openclaw\\openclaw.json' -Encoding UTF8 | ConvertFrom-Json
44
- $config.channels.agentspace.accounts.default.wps_sid
45
- `
46
-
47
- const result = nodes(
48
- action="invoke",
49
- node=nodeId,
50
- invokeCommand="system.run",
51
- invokeParamsJson={
52
- "command": ["powershell.exe", "-Command", command]
53
- }
54
- )
55
-
56
- if (!result.ok || !result.payload.success) {
57
- throw new Error("读取 wps_sid 失败: " + (result.payload.error || result.payload.stderr))
58
- }
59
-
60
- return result.payload.stdout.trim()
61
- }
62
-
63
- /**
64
- * 获取缓存的配置
65
- * @returns {Promise<{nodeId: string, wpsSid: string}>}
66
- */
67
- async getCachedConfig() {
68
- const now = Date.now()
69
-
70
- if (!this.cache.nodeId || (now - this.cache.lastUpdate) > this.cache.ttl) {
71
- this.cache.nodeId = this.getWindowsNodeId()
72
- this.cache.wpsSid = await this.getWpsSid(this.cache.nodeId)
73
- this.cache.lastUpdate = now
74
- }
75
-
76
- return {
77
- nodeId: this.cache.nodeId,
78
- wpsSid: this.cache.wpsSid
79
- }
80
- }
81
-
82
- /**
83
- * 执行 wps365 命令
84
- * @param {string} subcommand - 子命令(im, calendar, contacts, drive, dbsheet, meeting, user-current)
85
- * @param {string} action - 动作
86
- * @param {object} args - 参数
87
- * @returns {Promise<any>} 执行结果
88
- */
89
- async runCommand(subcommand, action, args = {}) {
90
- const { nodeId, wpsSid } = await this.getCachedConfig()
91
-
92
- // 构建命令
93
- let cmdArgs = []
94
-
95
- switch (subcommand) {
96
- case "im":
97
- cmdArgs = this.buildImCommand(action, args)
98
- break
99
- case "calendar":
100
- cmdArgs = this.buildCalendarCommand(action, args)
101
- break
102
- case "contacts":
103
- cmdArgs = this.buildContactsCommand(action, args)
104
- break
105
- case "drive":
106
- cmdArgs = this.buildDriveCommand(action, args)
107
- break
108
- case "dbsheet":
109
- cmdArgs = this.buildDbsheetCommand(action, args)
110
- break
111
- case "meeting":
112
- cmdArgs = this.buildMeetingCommand(action, args)
113
- break
114
- case "user-current":
115
- cmdArgs = this.buildUserCurrentCommand(action, args)
116
- break
117
- default:
118
- throw new Error(`不支持的子命令: ${subcommand}`)
119
- }
120
-
121
- // 执行命令
122
- const command = `$env:WPS_SID='${wpsSid}'; cd C:\\Users\\KSG\\.openclaw\\skills\\wps365-skill; python skills/${subcommand}/run.py ${cmdArgs.join(' ')}`
123
-
124
- const result = nodes(
125
- action="invoke",
126
- node=nodeId,
127
- invokeCommand="system.run",
128
- invokeParamsJson={
129
- "command": ["powershell.exe", "-Command", command],
130
- "timeout": 30000
131
- }
132
- )
133
-
134
- if (!result.ok || !result.payload.success) {
135
- throw new Error(`${subcommand} ${action} 执行失败: ${result.payload.stderr || result.payload.error}`)
136
- }
137
-
138
- return this.parseOutput(result.payload.stdout)
139
- }
140
-
141
- /**
142
- * 构建即时消息命令
143
- */
144
- buildImCommand(action, args) {
145
- const cmdArgs = []
146
-
147
- switch (action) {
148
- case "search":
149
- cmdArgs.push("search", `"${args.keyword}"`)
150
- if (args.page_size) cmdArgs.push("--page-size", args.page_size.toString())
151
- break
152
-
153
- case "send":
154
- cmdArgs.push("send", args.chat_id)
155
- if (args.message) {
156
- // 转义引号
157
- const escapedMessage = args.message.replace(/"/g, '\\"')
158
- cmdArgs.push(`"${escapedMessage}"`)
159
- }
160
- if (args.plain) cmdArgs.push("--plain")
161
- if (args.mention) {
162
- if (Array.isArray(args.mention)) {
163
- args.mention.forEach(userId => {
164
- cmdArgs.push("--mention", userId)
165
- })
166
- } else {
167
- cmdArgs.push("--mention", args.mention)
168
- }
169
- }
170
- if (args.type && args.type !== "text") {
171
- cmdArgs.push("--type", args.type)
172
- if (args[args.type]) {
173
- cmdArgs.push(`--${args.type}`, `'${JSON.stringify(args[args.type])}'`)
174
- }
175
- }
176
- break
177
-
178
- case "list":
179
- cmdArgs.push("list")
180
- if (args.page_size) cmdArgs.push("--page-size", args.page_size.toString())
181
- break
182
-
183
- case "history":
184
- cmdArgs.push("history", args.chat_id)
185
- if (args.start_time) cmdArgs.push("--start-time", args.start_time)
186
- if (args.end_time) cmdArgs.push("--end-time", args.end_time)
187
- if (args.page_size) cmdArgs.push("--page-size", args.page_size.toString())
188
- break
189
-
190
- case "recall":
191
- cmdArgs.push("recall", args.chat_id, args.message_id)
192
- break
193
-
194
- case "recent":
195
- cmdArgs.push("recent")
196
- if (args.filter_unread) cmdArgs.push("--filter-unread")
197
- if (args.filter_mention_me) cmdArgs.push("--filter-mention-me")
198
- if (args.page_size) cmdArgs.push("--page-size", args.page_size.toString())
199
- break
200
-
201
- case "search-messages":
202
- cmdArgs.push("search-messages")
203
- if (args.keyword) cmdArgs.push("--keyword", `"${args.keyword}"`)
204
- if (args.chat_ids) cmdArgs.push("--chat-ids", args.chat_ids)
205
- if (args.start_time) cmdArgs.push("--start-time", args.start_time)
206
- if (args.end_time) cmdArgs.push("--end-time", args.end_time)
207
- if (args.page_size) cmdArgs.push("--page-size", args.page_size.toString())
208
- break
209
-
210
- default:
211
- throw new Error(`不支持的 IM 动作: ${action}`)
212
- }
213
-
214
- return cmdArgs
215
- }
216
-
217
- /**
218
- * 构建日历命令(简化版)
219
- */
220
- buildCalendarCommand(action, args) {
221
- const cmdArgs = [action]
222
-
223
- switch (action) {
224
- case "create":
225
- if (args.summary) cmdArgs.push("--summary", `"${args.summary}"`)
226
- if (args.start) cmdArgs.push("--start", args.start)
227
- if (args.end) cmdArgs.push("--end", args.end)
228
- if (args.description) cmdArgs.push("--description", `"${args.description}"`)
229
- break
230
-
231
- case "list":
232
- if (args.start_time) cmdArgs.push("--start-time", args.start_time)
233
- if (args.end_time) cmdArgs.push("--end-time", args.end_time)
234
- if (args.page_size) cmdArgs.push("--page-size", args.page_size.toString())
235
- break
236
-
237
- default:
238
- // 直接传递其他参数
239
- Object.keys(args).forEach(key => {
240
- if (key.startsWith("--")) {
241
- cmdArgs.push(key, args[key])
242
- }
243
- })
244
- }
245
-
246
- return cmdArgs
247
- }
248
-
249
- /**
250
- * 构建联系人命令(简化版)
251
- */
252
- buildContactsCommand(action, args) {
253
- const cmdArgs = [action]
254
-
255
- if (action === "search" && args.keyword) {
256
- cmdArgs.push(`"${args.keyword}"`)
257
- }
258
-
259
- if (args.page_size) {
260
- cmdArgs.push("--page-size", args.page_size.toString())
261
- }
262
-
263
- return cmdArgs
264
- }
265
-
266
- /**
267
- * 构建云盘命令(简化版)
268
- */
269
- buildDriveCommand(action, args) {
270
- const cmdArgs = [action]
271
-
272
- switch (action) {
273
- case "upload":
274
- if (args.file_path) cmdArgs.push("--file", `"${args.file_path}"`)
275
- if (args.folder_token) cmdArgs.push("--folder-token", args.folder_token)
276
- break
277
-
278
- case "list":
279
- if (args.folder_token) cmdArgs.push("--folder-token", args.folder_token)
280
- if (args.page_size) cmdArgs.push("--page-size", args.page_size.toString())
281
- break
282
-
283
- case "search":
284
- if (args.keyword) cmdArgs.push("--keyword", `"${args.keyword}"`)
285
- if (args.page_size) cmdArgs.push("--page-size", args.page_size.toString())
286
- break
287
- }
288
-
289
- return cmdArgs
290
- }
291
-
292
- /**
293
- * 构建数据表命令(简化版)
294
- */
295
- buildDbsheetCommand(action, args) {
296
- const cmdArgs = [action]
297
-
298
- if (args.table_id) {
299
- cmdArgs.push("--table", args.table_id)
300
- }
301
-
302
- if (args.view_id) {
303
- cmdArgs.push("--view", args.view_id)
304
- }
305
-
306
- if (args.page_size) {
307
- cmdArgs.push("--page-size", args.page_size.toString())
308
- }
309
-
310
- return cmdArgs
311
- }
312
-
313
- /**
314
- * 构建会议命令(简化版)
315
- */
316
- buildMeetingCommand(action, args) {
317
- const cmdArgs = [action]
318
-
319
- if (args.meeting_id) {
320
- cmdArgs.push(args.meeting_id)
321
- }
322
-
323
- if (action === "create") {
324
- if (args.topic) cmdArgs.push("--topic", `"${args.topic}"`)
325
- if (args.start_time) cmdArgs.push("--start-time", args.start_time)
326
- if (args.duration) cmdArgs.push("--duration", args.duration.toString())
327
- }
328
-
329
- return cmdArgs
330
- }
331
-
332
- /**
333
- * 构建用户信息命令
334
- */
335
- buildUserCurrentCommand(action, args) {
336
- const cmdArgs = [action]
337
- return cmdArgs
338
- }
339
-
340
- /**
341
- * 解析命令输出
342
- */
343
- parseOutput(output) {
344
- try {
345
- // 尝试从输出中提取 JSON
346
- const jsonMatch = output.match(/```json\s*([\s\S]*?)\s*```/)
347
- if (jsonMatch) {
348
- return JSON.parse(jsonMatch[1])
349
- }
350
-
351
- // 尝试解析整个输出为 JSON(某些命令直接输出 JSON)
352
- try {
353
- const jsonStart = output.indexOf('{')
354
- const jsonEnd = output.lastIndexOf('}') + 1
355
- if (jsonStart >= 0 && jsonEnd > jsonStart) {
356
- const jsonStr = output.substring(jsonStart, jsonEnd)
357
- return JSON.parse(jsonStr)
358
- }
359
- } catch (e) {
360
- // 忽略 JSON 解析错误
361
- }
362
-
363
- // 返回原始输出
364
- return { raw_output: output.trim() }
365
- } catch (e) {
366
- return {
367
- raw_output: output.trim(),
368
- parse_error: e.message
369
- }
370
- }
371
- }
372
-
373
- // ========== 高级 API 方法 ==========
374
-
375
- /**
376
- * 搜索会话并发送消息
377
- * @param {string} groupName - 群名关键词
378
- * @param {string} message - 消息内容
379
- * @param {object} options - 选项
380
- */
381
- async sendMessageToGroup(groupName, message, options = {}) {
382
- // 搜索群聊
383
- const searchResult = await this.runCommand("im", "search", {
384
- keyword: groupName
385
- })
386
-
387
- if (!searchResult.items || searchResult.items.length === 0) {
388
- throw new Error(`未找到包含"${groupName}"的群聊`)
389
- }
390
-
391
- // 发送消息
392
- const sendResult = await this.runCommand("im", "send", {
393
- chat_id: searchResult.items[0].chat.id,
394
- message: message,
395
- plain: options.plain || false,
396
- mention: options.mention
397
- })
398
-
399
- return {
400
- chat_id: searchResult.items[0].chat.id,
401
- chat_name: searchResult.items[0].chat.name,
402
- message_id: sendResult.id,
403
- result: sendResult
404
- }
405
- }
406
-
407
- /**
408
- * 批量发送消息
409
- * @param {Array} messages - 消息列表 [{groupName, message, options}]
410
- */
411
- async batchSendMessages(messages) {
412
- const results = []
413
-
414
- for (const msg of messages) {
415
- try {
416
- const result = await this.sendMessageToGroup(
417
- msg.groupName,
418
- msg.message,
419
- msg.options || {}
420
- )
421
- results.push({
422
- success: true,
423
- group: msg.groupName,
424
- result: result
425
- })
426
- } catch (error) {
427
- results.push({
428
- success: false,
429
- group: msg.groupName,
430
- error: error.message
431
- })
432
- }
433
-
434
- // 避免频繁请求
435
- await this.sleep(500)
436
- }
437
-
438
- return results
439
- }
440
-
441
- /**
442
- * 创建日历事件并通知
443
- */
444
- async createEventAndNotify(event, groupName) {
445
- // 创建日历事件
446
- const eventResult = await this.runCommand("calendar", "create", {
447
- summary: event.summary,
448
- description: event.description,
449
- start: event.start_time,
450
- end: event.end_time,
451
- location: event.location
452
- })
453
-
454
- // 发送通知
455
- const message = `📅 新日历事件\n\n**事件**: ${event.summary}\n**时间**: ${event.start_time}\n**地点**: ${event.location || "未指定"}\n**描述**: ${event.description || "无"}`
456
-
457
- const notifyResult = await this.sendMessageToGroup(groupName, message)
458
-
459
- return {
460
- event: eventResult,
461
- notification: notifyResult
462
- }
463
- }
464
-
465
- /**
466
- * 上传文件并分享
467
- */
468
- async uploadAndShare(filePath, groupName, options = {}) {
469
- // 上传文件
470
- const uploadResult = await this.runCommand("drive", "upload", {
471
- file_path: filePath,
472
- folder_token: options.folder_token,
473
- file_name: options.file_name
474
- })
475
-
476
- // 获取文件信息
477
- const fileInfo = this.parseOutput(uploadResult.raw_output)
478
-
479
- // 发送文件消息
480
- if (fileInfo.link_id) {
481
- const sendResult = await this.runCommand("im", "send", {
482
- chat_id: await this.getChatIdByName(groupName),
483
- type: "file",
484
- file: JSON.stringify({
485
- type: "cloud",
486
- cloud: {
487
- id: fileInfo.link_id,
488
- link_url: fileInfo.url || `https://www.kdocs.cn/l/${fileInfo.link_id}`,
489
- link_id: fileInfo.link_id
490
- }
491
- })
492
- })
493
-
494
- return {
495
- upload: uploadResult,
496
- share: sendResult
497
- }
498
- }
499
-
500
- throw new Error("上传文件后未获取到 link_id")
501
- }
502
-
503
- /**
504
- * 根据群名获取 chat_id
505
- */
506
- async getChatIdByName(groupName) {
507
- const result = await this.runCommand("im", "search", {
508
- keyword: groupName
509
- })
510
-
511
- if (result.items && result.items.length > 0) {
512
- return result.items[0].chat.id
513
- }
514
-
515
- throw new Error(`未找到群聊: ${groupName}`)
516
- }
517
-
518
- /**
519
- * 等待函数
520
- */
521
- sleep(ms) {
522
- return new Promise(resolve => setTimeout(resolve, ms))
523
- }
524
- }
525
-
526
- // ========== 导出和使用示例 ==========
527
-
528
- // 创建全局实例
529
- const wps365 = new Wps365Remote()
530
-
531
- // 示例:发送消息到个人测试告警群
532
- async function demoSendMessage() {
533
- try {
534
- console.log("开始发送测试消息...")
535
-
536
- const result = await wps365.sendMessageToGroup(
537
- "个人测试告警群",
538
- "**远程技能调用测试**\n\n这是一条通过远程 WPS365 技能发送的测试消息。\n时间: " + new Date().toLocaleString(),
539
- { plain: false }
540
- )
541
-
542
- console.log("消息发送成功:")
543
- console.log(`- 群聊: ${result.chat_name} (${result.chat_id})`)
544
- console.log(`- 消息ID: ${result.message_id}`)
545
-
546
- return result
547
- } catch (error) {
548
- console.error("发送消息失败:", error.message)
549
- throw error
550
- }
551
- }
552
-
553
- // 示例:批量发送消息
554
- async function demoBatchSend() {
555
- const messages = [
556
- {
557
- groupName: "个人测试告警群",
558
- message: "系统监控正常,所有服务运行良好。",
559
- options: { plain: false }
560
- }
561
- ]
562
-
563
- return await wps365.batchSendMessages(messages)
564
- }
565
-
566
- // 示例:搜索会话
567
- async function demoSearchChats() {
568
- const result = await wps365.runCommand("im", "search", {
569
- keyword: "测试",
570
- page_size: 10
571
- })
572
-
573
- console.log(`找到 ${result.total} 个相关会话:`)
574
- result.items.forEach((item, index) => {
575
- console.log(`${index + 1}. [${item.chat.type}] ${item.chat.name} | ID: ${item.chat.id}`)
576
- })
577
-
578
- return result
579
- }
580
-
581
- // 导入 agentspace 安装器
582
- const { AgentspaceInstaller, quickInstallAgentspace, checkAgentspaceStatus } = require('./agentspace-installer')
583
-
584
- /**
585
- * 安装和配置 agentspace
586
- * 一键完成下载安装器、获取Windows节点配置、合并配置、重启OpenClaw
587
- */
588
- async function installAgentspace() {
589
- console.log('🔧 开始安装和配置 agentspace...')
590
- const installer = new AgentspaceInstaller()
591
- return await installer.installAndConfigure()
592
- }
593
-
594
- /**
595
- * 检查 agentspace 安装状态
596
- */
597
- function getAgentspaceStatus() {
598
- return checkAgentspaceStatus()
599
- }
600
-
601
- // 导出
602
- module.exports = {
603
- Wps365Remote,
604
- wps365,
605
- installAgentspace,
606
- getAgentspaceStatus,
607
- demo: {
608
- sendMessage: demoSendMessage,
609
- batchSend: demoBatchSend,
610
- searchChats: demoSearchChats
611
- }
612
- }