@f2a/openclaw-f2a 0.2.25 → 0.3.0
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/dist/connector.d.ts +42 -0
- package/dist/connector.d.ts.map +1 -1
- package/dist/connector.js +499 -0
- package/dist/connector.js.map +1 -1
- package/dist/contact-manager.d.ts +263 -0
- package/dist/contact-manager.d.ts.map +1 -0
- package/dist/contact-manager.js +872 -0
- package/dist/contact-manager.js.map +1 -0
- package/dist/contact-types.d.ts +287 -0
- package/dist/contact-types.d.ts.map +1 -0
- package/dist/contact-types.js +38 -0
- package/dist/contact-types.js.map +1 -0
- package/dist/handshake-protocol.d.ts +158 -0
- package/dist/handshake-protocol.d.ts.map +1 -0
- package/dist/handshake-protocol.js +441 -0
- package/dist/handshake-protocol.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/connector.d.ts
CHANGED
|
@@ -14,6 +14,13 @@ import type { OpenClawPlugin, OpenClawPluginApi, Tool, F2APluginConfig, AgentInf
|
|
|
14
14
|
* @returns 是否为有效的 libp2p Peer ID 格式
|
|
15
15
|
*/
|
|
16
16
|
export declare function isValidPeerId(peerId: string | undefined | null): peerId is string;
|
|
17
|
+
/** OpenClaw API Logger 类型 */
|
|
18
|
+
export interface ApiLogger {
|
|
19
|
+
info(message: string, ...args: unknown[]): void;
|
|
20
|
+
warn(message: string, ...args: unknown[]): void;
|
|
21
|
+
error(message: string, ...args: unknown[]): void;
|
|
22
|
+
debug?(message: string, ...args: unknown[]): void;
|
|
23
|
+
}
|
|
17
24
|
/** 广播结果类型 */
|
|
18
25
|
interface BroadcastResult {
|
|
19
26
|
agent: string;
|
|
@@ -37,6 +44,8 @@ export declare class F2AOpenClawAdapter implements OpenClawPlugin {
|
|
|
37
44
|
private _f2a?;
|
|
38
45
|
private _toolHandlers?;
|
|
39
46
|
private _claimHandlers?;
|
|
47
|
+
private _contactManager?;
|
|
48
|
+
private _handshakeProtocol?;
|
|
40
49
|
private _processedMessageHashes;
|
|
41
50
|
/** P1-3: 消息去重缓存最大条目数 */
|
|
42
51
|
private static readonly MAX_MESSAGE_HASH_CACHE_SIZE;
|
|
@@ -121,6 +130,15 @@ export declare class F2AOpenClawAdapter implements OpenClawPlugin {
|
|
|
121
130
|
* 获取认领处理器(延迟初始化)
|
|
122
131
|
*/
|
|
123
132
|
private get claimHandlers();
|
|
133
|
+
/**
|
|
134
|
+
* Issue #98: 获取联系人管理器(延迟初始化)
|
|
135
|
+
*/
|
|
136
|
+
private get contactManager();
|
|
137
|
+
/**
|
|
138
|
+
* Issue #99: 获取握手协议处理器(延迟初始化)
|
|
139
|
+
* 依赖 F2A 实例和 ContactManager
|
|
140
|
+
*/
|
|
141
|
+
private get handshakeProtocol();
|
|
124
142
|
/**
|
|
125
143
|
* 检查是否已初始化(用于判断是否需要启动服务)
|
|
126
144
|
*/
|
|
@@ -247,6 +265,30 @@ export declare class F2AOpenClawAdapter implements OpenClawPlugin {
|
|
|
247
265
|
* 解析 Agent 引用(公共方法,供测试和外部调用)
|
|
248
266
|
*/
|
|
249
267
|
resolveAgent(agentRef: string): Promise<AgentInfo | null>;
|
|
268
|
+
/**
|
|
269
|
+
* 处理通讯录工具
|
|
270
|
+
*/
|
|
271
|
+
private handleContacts;
|
|
272
|
+
/**
|
|
273
|
+
* 处理分组管理工具
|
|
274
|
+
*/
|
|
275
|
+
private handleContactGroups;
|
|
276
|
+
/**
|
|
277
|
+
* 处理好友请求工具
|
|
278
|
+
*/
|
|
279
|
+
private handleFriendRequest;
|
|
280
|
+
/**
|
|
281
|
+
* 处理待处理请求工具
|
|
282
|
+
*/
|
|
283
|
+
private handlePendingRequests;
|
|
284
|
+
/**
|
|
285
|
+
* 处理导出通讯录工具
|
|
286
|
+
*/
|
|
287
|
+
private handleContactsExport;
|
|
288
|
+
/**
|
|
289
|
+
* 处理导入通讯录工具
|
|
290
|
+
*/
|
|
291
|
+
private handleContactsImport;
|
|
250
292
|
/**
|
|
251
293
|
* 关闭插件,清理资源
|
|
252
294
|
* 只清理已初始化的资源
|
package/dist/connector.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connector.d.ts","sourceRoot":"","sources":["../src/connector.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EACjB,IAAI,EAIJ,eAAe,EACf,SAAS,EAIV,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"connector.d.ts","sourceRoot":"","sources":["../src/connector.ts"],"names":[],"mappings":"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"}
|
package/dist/connector.js
CHANGED
|
@@ -26,6 +26,10 @@ const task_guard_js_1 = require("./task-guard.js");
|
|
|
26
26
|
const tool_handlers_js_1 = require("./tool-handlers.js");
|
|
27
27
|
const claim_handlers_js_1 = require("./claim-handlers.js");
|
|
28
28
|
const network_1 = require("@f2a/network");
|
|
29
|
+
// Issue #98 & #99: 通讯录和握手机制
|
|
30
|
+
const contact_manager_js_1 = require("./contact-manager.js");
|
|
31
|
+
const handshake_protocol_js_1 = require("./handshake-protocol.js");
|
|
32
|
+
const contact_types_js_1 = require("./contact-types.js");
|
|
29
33
|
// ============================================================================
|
|
30
34
|
// 安全常量和验证工具
|
|
31
35
|
// ============================================================================
|
|
@@ -201,6 +205,9 @@ class F2AOpenClawAdapter {
|
|
|
201
205
|
// 处理器实例(延迟初始化)
|
|
202
206
|
_toolHandlers;
|
|
203
207
|
_claimHandlers;
|
|
208
|
+
// Issue #98 & #99: 通讯录和握手机制
|
|
209
|
+
_contactManager;
|
|
210
|
+
_handshakeProtocol;
|
|
204
211
|
// P1-3: 消息哈希去重缓存,防止恶意节点绕过 echo 检测
|
|
205
212
|
// 存储最近处理过的消息哈希,避免重复处理
|
|
206
213
|
_processedMessageHashes = new Map();
|
|
@@ -442,6 +449,28 @@ class F2AOpenClawAdapter {
|
|
|
442
449
|
}
|
|
443
450
|
return this._claimHandlers;
|
|
444
451
|
}
|
|
452
|
+
/**
|
|
453
|
+
* Issue #98: 获取联系人管理器(延迟初始化)
|
|
454
|
+
*/
|
|
455
|
+
get contactManager() {
|
|
456
|
+
if (!this._contactManager) {
|
|
457
|
+
const dataDir = this.getDefaultDataDir();
|
|
458
|
+
this._contactManager = new contact_manager_js_1.ContactManager(dataDir, this._logger);
|
|
459
|
+
this._logger?.info('[F2A Adapter] ContactManager 已初始化');
|
|
460
|
+
}
|
|
461
|
+
return this._contactManager;
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Issue #99: 获取握手协议处理器(延迟初始化)
|
|
465
|
+
* 依赖 F2A 实例和 ContactManager
|
|
466
|
+
*/
|
|
467
|
+
get handshakeProtocol() {
|
|
468
|
+
if (!this._handshakeProtocol && this._f2a && this._contactManager) {
|
|
469
|
+
this._handshakeProtocol = new handshake_protocol_js_1.HandshakeProtocol(this._f2a, this._contactManager, this._logger);
|
|
470
|
+
this._logger?.info('[F2A Adapter] HandshakeProtocol 已初始化');
|
|
471
|
+
}
|
|
472
|
+
return this._handshakeProtocol;
|
|
473
|
+
}
|
|
445
474
|
/**
|
|
446
475
|
* 检查是否已初始化(用于判断是否需要启动服务)
|
|
447
476
|
*/
|
|
@@ -1270,6 +1299,157 @@ class F2AOpenClawAdapter {
|
|
|
1270
1299
|
}
|
|
1271
1300
|
},
|
|
1272
1301
|
handler: this.toolHandlers.handleGetCapabilities.bind(this.toolHandlers)
|
|
1302
|
+
},
|
|
1303
|
+
// ========== Issue #98 & #99: 通讯录和握手机制工具 ==========
|
|
1304
|
+
{
|
|
1305
|
+
name: 'f2a_contacts',
|
|
1306
|
+
description: '管理通讯录联系人。Actions: list(列出联系人), get(获取详情), add(添加), remove(删除), update(更新), block(拉黑), unblock(解除拉黑)',
|
|
1307
|
+
parameters: {
|
|
1308
|
+
action: {
|
|
1309
|
+
type: 'string',
|
|
1310
|
+
description: '操作类型: list, get, add, remove, update, block, unblock',
|
|
1311
|
+
required: true,
|
|
1312
|
+
enum: ['list', 'get', 'add', 'remove', 'update', 'block', 'unblock']
|
|
1313
|
+
},
|
|
1314
|
+
contact_id: {
|
|
1315
|
+
type: 'string',
|
|
1316
|
+
description: '联系人 ID(get/remove/update/block/unblock 时需要)',
|
|
1317
|
+
required: false
|
|
1318
|
+
},
|
|
1319
|
+
peer_id: {
|
|
1320
|
+
type: 'string',
|
|
1321
|
+
description: 'Peer ID(add 时需要,get/remove 时可选)',
|
|
1322
|
+
required: false
|
|
1323
|
+
},
|
|
1324
|
+
name: {
|
|
1325
|
+
type: 'string',
|
|
1326
|
+
description: '联系人名称(add/update 时需要)',
|
|
1327
|
+
required: false
|
|
1328
|
+
},
|
|
1329
|
+
groups: {
|
|
1330
|
+
type: 'array',
|
|
1331
|
+
description: '分组列表',
|
|
1332
|
+
required: false
|
|
1333
|
+
},
|
|
1334
|
+
tags: {
|
|
1335
|
+
type: 'array',
|
|
1336
|
+
description: '标签列表',
|
|
1337
|
+
required: false
|
|
1338
|
+
},
|
|
1339
|
+
notes: {
|
|
1340
|
+
type: 'string',
|
|
1341
|
+
description: '备注信息',
|
|
1342
|
+
required: false
|
|
1343
|
+
},
|
|
1344
|
+
status: {
|
|
1345
|
+
type: 'string',
|
|
1346
|
+
description: '按状态过滤(list 时可选): stranger, pending, friend, blocked',
|
|
1347
|
+
required: false,
|
|
1348
|
+
enum: ['stranger', 'pending', 'friend', 'blocked']
|
|
1349
|
+
},
|
|
1350
|
+
group: {
|
|
1351
|
+
type: 'string',
|
|
1352
|
+
description: '按分组过滤(list 时可选)',
|
|
1353
|
+
required: false
|
|
1354
|
+
}
|
|
1355
|
+
},
|
|
1356
|
+
handler: this.handleContacts.bind(this)
|
|
1357
|
+
},
|
|
1358
|
+
{
|
|
1359
|
+
name: 'f2a_contact_groups',
|
|
1360
|
+
description: '管理联系人分组。Actions: list(列出分组), create(创建), update(更新), delete(删除)',
|
|
1361
|
+
parameters: {
|
|
1362
|
+
action: {
|
|
1363
|
+
type: 'string',
|
|
1364
|
+
description: '操作类型: list, create, update, delete',
|
|
1365
|
+
required: true,
|
|
1366
|
+
enum: ['list', 'create', 'update', 'delete']
|
|
1367
|
+
},
|
|
1368
|
+
group_id: {
|
|
1369
|
+
type: 'string',
|
|
1370
|
+
description: '分组 ID(update/delete 时需要)',
|
|
1371
|
+
required: false
|
|
1372
|
+
},
|
|
1373
|
+
name: {
|
|
1374
|
+
type: 'string',
|
|
1375
|
+
description: '分组名称(create/update 时需要)',
|
|
1376
|
+
required: false
|
|
1377
|
+
},
|
|
1378
|
+
description: {
|
|
1379
|
+
type: 'string',
|
|
1380
|
+
description: '分组描述',
|
|
1381
|
+
required: false
|
|
1382
|
+
},
|
|
1383
|
+
color: {
|
|
1384
|
+
type: 'string',
|
|
1385
|
+
description: '分组颜色(十六进制,如 #FF5733)',
|
|
1386
|
+
required: false
|
|
1387
|
+
}
|
|
1388
|
+
},
|
|
1389
|
+
handler: this.handleContactGroups.bind(this)
|
|
1390
|
+
},
|
|
1391
|
+
{
|
|
1392
|
+
name: 'f2a_friend_request',
|
|
1393
|
+
description: '发送好友请求给指定 Agent',
|
|
1394
|
+
parameters: {
|
|
1395
|
+
peer_id: {
|
|
1396
|
+
type: 'string',
|
|
1397
|
+
description: '目标 Agent 的 Peer ID',
|
|
1398
|
+
required: true
|
|
1399
|
+
},
|
|
1400
|
+
message: {
|
|
1401
|
+
type: 'string',
|
|
1402
|
+
description: '附加消息',
|
|
1403
|
+
required: false
|
|
1404
|
+
}
|
|
1405
|
+
},
|
|
1406
|
+
handler: this.handleFriendRequest.bind(this)
|
|
1407
|
+
},
|
|
1408
|
+
{
|
|
1409
|
+
name: 'f2a_pending_requests',
|
|
1410
|
+
description: '查看和处理待处理的好友请求。Actions: list(列出请求), accept(接受), reject(拒绝)',
|
|
1411
|
+
parameters: {
|
|
1412
|
+
action: {
|
|
1413
|
+
type: 'string',
|
|
1414
|
+
description: '操作类型: list, accept, reject',
|
|
1415
|
+
required: true,
|
|
1416
|
+
enum: ['list', 'accept', 'reject']
|
|
1417
|
+
},
|
|
1418
|
+
request_id: {
|
|
1419
|
+
type: 'string',
|
|
1420
|
+
description: '请求 ID(accept/reject 时需要)',
|
|
1421
|
+
required: false
|
|
1422
|
+
},
|
|
1423
|
+
reason: {
|
|
1424
|
+
type: 'string',
|
|
1425
|
+
description: '拒绝原因(reject 时可选)',
|
|
1426
|
+
required: false
|
|
1427
|
+
}
|
|
1428
|
+
},
|
|
1429
|
+
handler: this.handlePendingRequests.bind(this)
|
|
1430
|
+
},
|
|
1431
|
+
{
|
|
1432
|
+
name: 'f2a_contacts_export',
|
|
1433
|
+
description: '导出通讯录数据',
|
|
1434
|
+
parameters: {},
|
|
1435
|
+
handler: this.handleContactsExport.bind(this)
|
|
1436
|
+
},
|
|
1437
|
+
{
|
|
1438
|
+
name: 'f2a_contacts_import',
|
|
1439
|
+
description: '导入通讯录数据',
|
|
1440
|
+
parameters: {
|
|
1441
|
+
data: {
|
|
1442
|
+
type: 'object',
|
|
1443
|
+
description: '导入的通讯录数据(JSON 格式)',
|
|
1444
|
+
required: true
|
|
1445
|
+
},
|
|
1446
|
+
merge: {
|
|
1447
|
+
type: 'boolean',
|
|
1448
|
+
description: '是否合并(true)或覆盖(false),默认 true',
|
|
1449
|
+
required: false
|
|
1450
|
+
}
|
|
1451
|
+
},
|
|
1452
|
+
handler: this.handleContactsImport.bind(this)
|
|
1273
1453
|
}
|
|
1274
1454
|
];
|
|
1275
1455
|
}
|
|
@@ -1605,6 +1785,313 @@ class F2AOpenClawAdapter {
|
|
|
1605
1785
|
(a.displayName?.toLowerCase().includes(agentRef.toLowerCase()) ?? false));
|
|
1606
1786
|
return fuzzy || null;
|
|
1607
1787
|
}
|
|
1788
|
+
// ============================================================================
|
|
1789
|
+
// Issue #98 & #99: 通讯录和握手机制工具处理方法
|
|
1790
|
+
// ============================================================================
|
|
1791
|
+
/**
|
|
1792
|
+
* 处理通讯录工具
|
|
1793
|
+
*/
|
|
1794
|
+
async handleContacts(params, _context) {
|
|
1795
|
+
try {
|
|
1796
|
+
const cm = this.contactManager;
|
|
1797
|
+
switch (params.action) {
|
|
1798
|
+
case 'list': {
|
|
1799
|
+
const filter = {};
|
|
1800
|
+
if (params.status) {
|
|
1801
|
+
filter.status = params.status;
|
|
1802
|
+
}
|
|
1803
|
+
if (params.group) {
|
|
1804
|
+
filter.group = params.group;
|
|
1805
|
+
}
|
|
1806
|
+
const contacts = cm.getContacts(filter, { field: 'name', order: 'asc' });
|
|
1807
|
+
const stats = cm.getStats();
|
|
1808
|
+
return {
|
|
1809
|
+
content: `📋 **通讯录** (${stats.total} 个联系人)\n\n` +
|
|
1810
|
+
contacts.map(c => {
|
|
1811
|
+
const statusIcon = {
|
|
1812
|
+
[contact_types_js_1.FriendStatus.FRIEND]: '💚',
|
|
1813
|
+
[contact_types_js_1.FriendStatus.STRANGER]: '⚪',
|
|
1814
|
+
[contact_types_js_1.FriendStatus.PENDING]: '🟡',
|
|
1815
|
+
[contact_types_js_1.FriendStatus.BLOCKED]: '🔴',
|
|
1816
|
+
}[c.status];
|
|
1817
|
+
return `${statusIcon} **${c.name}**\n Peer: ${c.peerId.slice(0, 16)}...\n 信誉: ${c.reputation} | 状态: ${c.status}`;
|
|
1818
|
+
}).join('\n\n') || '暂无联系人'
|
|
1819
|
+
};
|
|
1820
|
+
}
|
|
1821
|
+
case 'get': {
|
|
1822
|
+
let contact;
|
|
1823
|
+
if (params.contact_id) {
|
|
1824
|
+
contact = cm.getContact(params.contact_id);
|
|
1825
|
+
}
|
|
1826
|
+
else if (params.peer_id) {
|
|
1827
|
+
contact = cm.getContactByPeerId(params.peer_id);
|
|
1828
|
+
}
|
|
1829
|
+
else {
|
|
1830
|
+
return { content: '❌ 需要提供 contact_id 或 peer_id' };
|
|
1831
|
+
}
|
|
1832
|
+
if (!contact) {
|
|
1833
|
+
return { content: '❌ 联系人不存在' };
|
|
1834
|
+
}
|
|
1835
|
+
return {
|
|
1836
|
+
content: `👤 **${contact.name}**\n` +
|
|
1837
|
+
` ID: ${contact.id}\n` +
|
|
1838
|
+
` Peer ID: ${contact.peerId}\n` +
|
|
1839
|
+
` 状态: ${contact.status}\n` +
|
|
1840
|
+
` 信誉: ${contact.reputation}\n` +
|
|
1841
|
+
` 分组: ${contact.groups.join(', ') || '无'}\n` +
|
|
1842
|
+
` 标签: ${contact.tags.join(', ') || '无'}\n` +
|
|
1843
|
+
` 最后通信: ${contact.lastCommunicationTime ? new Date(contact.lastCommunicationTime).toLocaleString() : '从未'}\n` +
|
|
1844
|
+
(contact.notes ? ` 备注: ${contact.notes}` : '')
|
|
1845
|
+
};
|
|
1846
|
+
}
|
|
1847
|
+
case 'add': {
|
|
1848
|
+
if (!params.peer_id || !params.name) {
|
|
1849
|
+
return { content: '❌ 需要提供 peer_id 和 name' };
|
|
1850
|
+
}
|
|
1851
|
+
const contact = cm.addContact({
|
|
1852
|
+
name: params.name,
|
|
1853
|
+
peerId: params.peer_id,
|
|
1854
|
+
groups: params.groups,
|
|
1855
|
+
tags: params.tags,
|
|
1856
|
+
notes: params.notes,
|
|
1857
|
+
});
|
|
1858
|
+
return { content: `✅ 已添加联系人: ${contact.name} (${contact.peerId.slice(0, 16)})` };
|
|
1859
|
+
}
|
|
1860
|
+
case 'remove': {
|
|
1861
|
+
let contactId = params.contact_id;
|
|
1862
|
+
if (!contactId && params.peer_id) {
|
|
1863
|
+
const contact = cm.getContactByPeerId(params.peer_id);
|
|
1864
|
+
contactId = contact?.id;
|
|
1865
|
+
}
|
|
1866
|
+
if (!contactId) {
|
|
1867
|
+
return { content: '❌ 需要提供 contact_id 或 peer_id' };
|
|
1868
|
+
}
|
|
1869
|
+
const success = cm.removeContact(contactId);
|
|
1870
|
+
return { content: success ? '✅ 已删除联系人' : '❌ 联系人不存在' };
|
|
1871
|
+
}
|
|
1872
|
+
case 'update': {
|
|
1873
|
+
let contactId = params.contact_id;
|
|
1874
|
+
if (!contactId && params.peer_id) {
|
|
1875
|
+
const contact = cm.getContactByPeerId(params.peer_id);
|
|
1876
|
+
contactId = contact?.id;
|
|
1877
|
+
}
|
|
1878
|
+
if (!contactId) {
|
|
1879
|
+
return { content: '❌ 需要提供 contact_id 或 peer_id' };
|
|
1880
|
+
}
|
|
1881
|
+
const contact = cm.updateContact(contactId, {
|
|
1882
|
+
name: params.name,
|
|
1883
|
+
groups: params.groups,
|
|
1884
|
+
tags: params.tags,
|
|
1885
|
+
notes: params.notes,
|
|
1886
|
+
});
|
|
1887
|
+
return { content: contact ? `✅ 已更新联系人: ${contact.name}` : '❌ 联系人不存在' };
|
|
1888
|
+
}
|
|
1889
|
+
case 'block': {
|
|
1890
|
+
let contactId = params.contact_id;
|
|
1891
|
+
if (!contactId && params.peer_id) {
|
|
1892
|
+
const contact = cm.getContactByPeerId(params.peer_id);
|
|
1893
|
+
contactId = contact?.id;
|
|
1894
|
+
}
|
|
1895
|
+
if (!contactId) {
|
|
1896
|
+
return { content: '❌ 需要提供 contact_id 或 peer_id' };
|
|
1897
|
+
}
|
|
1898
|
+
const success = cm.blockContact(contactId);
|
|
1899
|
+
return { content: success ? '✅ 已拉黑联系人' : '❌ 联系人不存在' };
|
|
1900
|
+
}
|
|
1901
|
+
case 'unblock': {
|
|
1902
|
+
let contactId = params.contact_id;
|
|
1903
|
+
if (!contactId && params.peer_id) {
|
|
1904
|
+
const contact = cm.getContactByPeerId(params.peer_id);
|
|
1905
|
+
contactId = contact?.id;
|
|
1906
|
+
}
|
|
1907
|
+
if (!contactId) {
|
|
1908
|
+
return { content: '❌ 需要提供 contact_id 或 peer_id' };
|
|
1909
|
+
}
|
|
1910
|
+
const success = cm.unblockContact(contactId);
|
|
1911
|
+
return { content: success ? '✅ 已解除拉黑' : '❌ 联系人不存在' };
|
|
1912
|
+
}
|
|
1913
|
+
default:
|
|
1914
|
+
return { content: '❌ 未知操作' };
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
catch (err) {
|
|
1918
|
+
return { content: `❌ 操作失败: ${err instanceof Error ? err.message : String(err)}` };
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
/**
|
|
1922
|
+
* 处理分组管理工具
|
|
1923
|
+
*/
|
|
1924
|
+
async handleContactGroups(params, _context) {
|
|
1925
|
+
try {
|
|
1926
|
+
const cm = this.contactManager;
|
|
1927
|
+
switch (params.action) {
|
|
1928
|
+
case 'list': {
|
|
1929
|
+
const groups = cm.getGroups();
|
|
1930
|
+
return {
|
|
1931
|
+
content: `📁 **分组列表** (${groups.length} 个)\n\n` +
|
|
1932
|
+
groups.map(g => `• **${g.name}** (${g.id})\n ${g.description || '无描述'}`).join('\n\n')
|
|
1933
|
+
};
|
|
1934
|
+
}
|
|
1935
|
+
case 'create': {
|
|
1936
|
+
if (!params.name) {
|
|
1937
|
+
return { content: '❌ 需要提供分组名称' };
|
|
1938
|
+
}
|
|
1939
|
+
const group = cm.createGroup({
|
|
1940
|
+
name: params.name,
|
|
1941
|
+
description: params.description,
|
|
1942
|
+
color: params.color,
|
|
1943
|
+
});
|
|
1944
|
+
return { content: `✅ 已创建分组: ${group.name}` };
|
|
1945
|
+
}
|
|
1946
|
+
case 'update': {
|
|
1947
|
+
if (!params.group_id) {
|
|
1948
|
+
return { content: '❌ 需要提供 group_id' };
|
|
1949
|
+
}
|
|
1950
|
+
const group = cm.updateGroup(params.group_id, {
|
|
1951
|
+
name: params.name,
|
|
1952
|
+
description: params.description,
|
|
1953
|
+
color: params.color,
|
|
1954
|
+
});
|
|
1955
|
+
return { content: group ? `✅ 已更新分组: ${group.name}` : '❌ 分组不存在' };
|
|
1956
|
+
}
|
|
1957
|
+
case 'delete': {
|
|
1958
|
+
if (!params.group_id) {
|
|
1959
|
+
return { content: '❌ 需要提供 group_id' };
|
|
1960
|
+
}
|
|
1961
|
+
const success = cm.deleteGroup(params.group_id);
|
|
1962
|
+
return { content: success ? '✅ 已删除分组' : '❌ 无法删除(分组不存在或为默认分组)' };
|
|
1963
|
+
}
|
|
1964
|
+
default:
|
|
1965
|
+
return { content: '❌ 未知操作' };
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
catch (err) {
|
|
1969
|
+
return { content: `❌ 操作失败: ${err instanceof Error ? err.message : String(err)}` };
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
/**
|
|
1973
|
+
* 处理好友请求工具
|
|
1974
|
+
*/
|
|
1975
|
+
async handleFriendRequest(params, _context) {
|
|
1976
|
+
try {
|
|
1977
|
+
if (!this._f2a) {
|
|
1978
|
+
return { content: '❌ F2A 实例未初始化' };
|
|
1979
|
+
}
|
|
1980
|
+
if (!this._contactManager) {
|
|
1981
|
+
// 确保联系人管理器已初始化
|
|
1982
|
+
this.contactManager;
|
|
1983
|
+
}
|
|
1984
|
+
if (!this._handshakeProtocol) {
|
|
1985
|
+
// 初始化握手协议
|
|
1986
|
+
this.handshakeProtocol;
|
|
1987
|
+
}
|
|
1988
|
+
if (!this._handshakeProtocol) {
|
|
1989
|
+
return { content: '❌ 握手协议未初始化' };
|
|
1990
|
+
}
|
|
1991
|
+
const requestId = await this._handshakeProtocol.sendFriendRequest(params.peer_id, params.message);
|
|
1992
|
+
if (requestId) {
|
|
1993
|
+
return { content: `✅ 好友请求已发送\n请求 ID: ${requestId}\n等待对方响应...` };
|
|
1994
|
+
}
|
|
1995
|
+
else {
|
|
1996
|
+
return { content: '❌ 发送好友请求失败' };
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
catch (err) {
|
|
2000
|
+
return { content: `❌ 发送失败: ${err instanceof Error ? err.message : String(err)}` };
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
/**
|
|
2004
|
+
* 处理待处理请求工具
|
|
2005
|
+
*/
|
|
2006
|
+
async handlePendingRequests(params, _context) {
|
|
2007
|
+
try {
|
|
2008
|
+
const cm = this.contactManager;
|
|
2009
|
+
switch (params.action) {
|
|
2010
|
+
case 'list': {
|
|
2011
|
+
const pending = cm.getPendingHandshakes();
|
|
2012
|
+
if (pending.length === 0) {
|
|
2013
|
+
return { content: '📭 暂无待处理的好友请求' };
|
|
2014
|
+
}
|
|
2015
|
+
return {
|
|
2016
|
+
content: `📬 **待处理的好友请求** (${pending.length} 个)\n\n` +
|
|
2017
|
+
pending.map(p => `• **${p.fromName}**\n Peer: ${p.from.slice(0, 16)}...\n 请求 ID: ${p.requestId}\n 收到: ${new Date(p.receivedAt).toLocaleString()}` +
|
|
2018
|
+
(p.message ? `\n 消息: ${p.message}` : '')).join('\n\n')
|
|
2019
|
+
};
|
|
2020
|
+
}
|
|
2021
|
+
case 'accept': {
|
|
2022
|
+
if (!params.request_id) {
|
|
2023
|
+
return { content: '❌ 需要提供 request_id' };
|
|
2024
|
+
}
|
|
2025
|
+
if (!this._handshakeProtocol) {
|
|
2026
|
+
return { content: '❌ 握手协议未初始化' };
|
|
2027
|
+
}
|
|
2028
|
+
const success = await this._handshakeProtocol.acceptRequest(params.request_id);
|
|
2029
|
+
return { content: success ? '✅ 已接受好友请求,双方已成为好友' : '❌ 接受失败' };
|
|
2030
|
+
}
|
|
2031
|
+
case 'reject': {
|
|
2032
|
+
if (!params.request_id) {
|
|
2033
|
+
return { content: '❌ 需要提供 request_id' };
|
|
2034
|
+
}
|
|
2035
|
+
if (!this._handshakeProtocol) {
|
|
2036
|
+
return { content: '❌ 握手协议未初始化' };
|
|
2037
|
+
}
|
|
2038
|
+
const success = await this._handshakeProtocol.rejectRequest(params.request_id, params.reason);
|
|
2039
|
+
return { content: success ? '✅ 已拒绝好友请求' : '❌ 拒绝失败' };
|
|
2040
|
+
}
|
|
2041
|
+
default:
|
|
2042
|
+
return { content: '❌ 未知操作' };
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
catch (err) {
|
|
2046
|
+
return { content: `❌ 操作失败: ${err instanceof Error ? err.message : String(err)}` };
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
/**
|
|
2050
|
+
* 处理导出通讯录工具
|
|
2051
|
+
*/
|
|
2052
|
+
async handleContactsExport(_params, _context) {
|
|
2053
|
+
try {
|
|
2054
|
+
const cm = this.contactManager;
|
|
2055
|
+
const data = cm.exportContacts(this._f2a?.peerId);
|
|
2056
|
+
return {
|
|
2057
|
+
content: `📤 **通讯录导出成功**\n\n` +
|
|
2058
|
+
`联系人: ${data.contacts.length} 个\n` +
|
|
2059
|
+
`分组: ${data.groups.length} 个\n` +
|
|
2060
|
+
`导出时间: ${new Date(data.exportedAt).toLocaleString()}\n\n` +
|
|
2061
|
+
'```json\n' + JSON.stringify(data, null, 2) + '\n```',
|
|
2062
|
+
data,
|
|
2063
|
+
};
|
|
2064
|
+
}
|
|
2065
|
+
catch (err) {
|
|
2066
|
+
return { content: `❌ 导出失败: ${err instanceof Error ? err.message : String(err)}` };
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
/**
|
|
2070
|
+
* 处理导入通讯录工具
|
|
2071
|
+
*/
|
|
2072
|
+
async handleContactsImport(params, _context) {
|
|
2073
|
+
try {
|
|
2074
|
+
const cm = this.contactManager;
|
|
2075
|
+
const result = cm.importContacts(params.data, params.merge ?? true);
|
|
2076
|
+
if (result.success) {
|
|
2077
|
+
return {
|
|
2078
|
+
content: `📥 **通讯录导入完成**\n\n` +
|
|
2079
|
+
`✅ 导入联系人: ${result.importedContacts} 个\n` +
|
|
2080
|
+
`✅ 导入分组: ${result.importedGroups} 个\n` +
|
|
2081
|
+
`⏭️ 跳过联系人: ${result.skippedContacts} 个` +
|
|
2082
|
+
(result.errors.length ? `\n\n⚠️ 错误:\n${result.errors.join('\n')}` : '')
|
|
2083
|
+
};
|
|
2084
|
+
}
|
|
2085
|
+
else {
|
|
2086
|
+
return {
|
|
2087
|
+
content: `❌ 导入失败\n\n错误:\n${result.errors.join('\n')}`
|
|
2088
|
+
};
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
catch (err) {
|
|
2092
|
+
return { content: `❌ 导入失败: ${err instanceof Error ? err.message : String(err)}` };
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
1608
2095
|
/**
|
|
1609
2096
|
* 关闭插件,清理资源
|
|
1610
2097
|
* 只清理已初始化的资源
|
|
@@ -1616,6 +2103,18 @@ class F2AOpenClawAdapter {
|
|
|
1616
2103
|
clearInterval(this.pollTimer);
|
|
1617
2104
|
this.pollTimer = undefined;
|
|
1618
2105
|
}
|
|
2106
|
+
// Issue #99: 关闭握手协议
|
|
2107
|
+
if (this._handshakeProtocol) {
|
|
2108
|
+
this._handshakeProtocol.shutdown();
|
|
2109
|
+
this._logger?.info('[F2A Adapter] HandshakeProtocol 已关闭');
|
|
2110
|
+
this._handshakeProtocol = undefined;
|
|
2111
|
+
}
|
|
2112
|
+
// Issue #98: 刷新通讯录数据
|
|
2113
|
+
if (this._contactManager) {
|
|
2114
|
+
this._contactManager.flush();
|
|
2115
|
+
this._logger?.info('[F2A Adapter] ContactManager 数据已保存');
|
|
2116
|
+
this._contactManager = undefined;
|
|
2117
|
+
}
|
|
1619
2118
|
// 停止 F2A 实例(新架构直接管理)
|
|
1620
2119
|
if (this._f2a) {
|
|
1621
2120
|
try {
|