acp-ts 1.1.3 → 1.1.5
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/assets/agent.png +0 -0
- package/assets/human.png +0 -0
- package/assets/openclaw.png +0 -0
- package/dist/agentcp.d.ts +113 -0
- package/dist/agentcp.js +292 -3
- package/dist/agentws.d.ts +17 -0
- package/dist/agentws.js +23 -0
- package/dist/datamanager.js +8 -1
- package/dist/group/client.d.ts +41 -0
- package/dist/group/client.js +135 -0
- package/dist/group/cursor_store.d.ts +36 -0
- package/dist/group/cursor_store.js +94 -0
- package/dist/group/events.d.ts +48 -0
- package/dist/group/events.js +130 -0
- package/dist/group/index.d.ts +10 -0
- package/dist/group/index.js +58 -0
- package/dist/group/message_store.d.ts +71 -0
- package/dist/group/message_store.js +366 -0
- package/dist/group/operations.d.ts +115 -0
- package/dist/group/operations.js +592 -0
- package/dist/group/types.d.ts +266 -0
- package/dist/group/types.js +140 -0
- package/dist/heartbeat.d.ts +6 -0
- package/dist/heartbeat.js +20 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +15 -0
- package/dist/interfaces.d.ts +110 -0
- package/dist/server.js +793 -33
- package/dist/websocket.d.ts +16 -0
- package/dist/websocket.js +106 -4
- package/package.json +3 -2
package/assets/agent.png
ADDED
|
Binary file
|
package/assets/human.png
ADDED
|
Binary file
|
|
Binary file
|
package/dist/agentcp.d.ts
CHANGED
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
import { IAgentCP, IAgentIdentity } from "./interfaces";
|
|
2
2
|
import { MessageStore } from "./messagestore";
|
|
3
3
|
import { AgentMdOptions } from "./agentmd";
|
|
4
|
+
import { ACPGroupClient, GroupOperations, ACPGroupEventHandler, CursorStore, GroupMessageStore, GroupMessage } from "./group";
|
|
4
5
|
declare class AgentCP implements IAgentCP {
|
|
5
6
|
private seedPassword;
|
|
6
7
|
private apUrl;
|
|
7
8
|
private msgUrl;
|
|
8
9
|
private activeAid;
|
|
10
|
+
private _basePath;
|
|
9
11
|
private _messageStore;
|
|
10
12
|
private agentMdPath;
|
|
11
13
|
private agentMdUploaded;
|
|
12
14
|
private autoGenerateAgentMd;
|
|
13
15
|
private agentMdOptions;
|
|
16
|
+
groupClient: ACPGroupClient | null;
|
|
17
|
+
groupOps: GroupOperations | null;
|
|
18
|
+
groupMessageStore: GroupMessageStore | null;
|
|
19
|
+
private _groupTargetAid;
|
|
20
|
+
private _groupSessionId;
|
|
21
|
+
private _persistGroupMessages;
|
|
14
22
|
get messageStore(): MessageStore;
|
|
15
23
|
constructor(apiUrl: string, seedPassword?: string, basePath?: string, options?: {
|
|
16
24
|
persistMessages?: boolean;
|
|
25
|
+
persistGroupMessages?: boolean;
|
|
17
26
|
});
|
|
18
27
|
/**
|
|
19
28
|
* 初始化并预热加密模块
|
|
@@ -43,6 +52,110 @@ declare class AgentCP implements IAgentCP {
|
|
|
43
52
|
private getAgentMdMarkerPath;
|
|
44
53
|
private checkAgentMdMarker;
|
|
45
54
|
private saveAgentMdMarker;
|
|
55
|
+
/**
|
|
56
|
+
* 获取群组目标AID
|
|
57
|
+
*/
|
|
58
|
+
getGroupTargetAid(): string;
|
|
59
|
+
/**
|
|
60
|
+
* 初始化群组客户端。
|
|
61
|
+
* 需要外部提供 sendRaw 函数和 sessionId,因为 AgentCP 本身不管理 WebSocket 连接。
|
|
62
|
+
*
|
|
63
|
+
* @param sendRaw 发送原始消息的函数: (message, to, sessionId) => void
|
|
64
|
+
* @param sessionId 与 group.{issuer} 的会话ID
|
|
65
|
+
* @param targetAid 目标群组AID,默认 "group.{issuer}"
|
|
66
|
+
*/
|
|
67
|
+
initGroupClient(sendRaw: (message: string, to: string, sessionId: string) => void, sessionId: string, targetAid?: string): void;
|
|
68
|
+
/**
|
|
69
|
+
* 初始化跨AP群组客户端。
|
|
70
|
+
*
|
|
71
|
+
* @param sendRaw 发送原始消息的函数
|
|
72
|
+
* @param sessionId 与目标群组AID的会话ID
|
|
73
|
+
* @param targetAid 目标群组AID (如 "group.aid.com")
|
|
74
|
+
*/
|
|
75
|
+
initGroupClientCrossAp(sendRaw: (message: string, to: string, sessionId: string) => void, sessionId: string, targetAid: string): void;
|
|
76
|
+
/**
|
|
77
|
+
* 处理群组协议消息路由。
|
|
78
|
+
* 当收到 session_message 时,检查 sender 是否为群组目标AID,
|
|
79
|
+
* 如果是则路由到 groupClient.handleIncoming()。
|
|
80
|
+
*
|
|
81
|
+
* @returns true 如果消息被群组客户端处理
|
|
82
|
+
*/
|
|
83
|
+
handleGroupMessage(message: any): boolean;
|
|
84
|
+
/**
|
|
85
|
+
* 设置群组事件处理器
|
|
86
|
+
*/
|
|
87
|
+
setGroupEventHandler(handler: ACPGroupEventHandler): void;
|
|
88
|
+
/**
|
|
89
|
+
* 设置群组游标存储
|
|
90
|
+
*/
|
|
91
|
+
setGroupCursorStore(store: CursorStore): void;
|
|
92
|
+
/**
|
|
93
|
+
* 关闭群组客户端
|
|
94
|
+
*/
|
|
95
|
+
closeGroupClient(): void;
|
|
96
|
+
/**
|
|
97
|
+
* 初始化群消息持久化存储
|
|
98
|
+
*/
|
|
99
|
+
initGroupMessageStore(options?: {
|
|
100
|
+
maxMessagesPerGroup?: number;
|
|
101
|
+
maxEventsPerGroup?: number;
|
|
102
|
+
}): Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* 确保群消息持久化存储已初始化并加载完成。
|
|
105
|
+
* 如果已初始化则直接返回,否则初始化并等待磁盘数据加载完成。
|
|
106
|
+
*/
|
|
107
|
+
ensureGroupMessageStore(): Promise<void>;
|
|
108
|
+
/**
|
|
109
|
+
* 从服务端拉取群组列表并同步到本地存储。
|
|
110
|
+
* 返回群组列表(含 group_id, name, member_count 等)。
|
|
111
|
+
*/
|
|
112
|
+
syncGroupList(): Promise<Array<{
|
|
113
|
+
group_id: string;
|
|
114
|
+
name: string;
|
|
115
|
+
member_count?: number;
|
|
116
|
+
}>>;
|
|
117
|
+
/**
|
|
118
|
+
* 获取本地存储的群组列表。
|
|
119
|
+
* 如果持久化存储已初始化,从存储中读取;否则返回空数组。
|
|
120
|
+
*/
|
|
121
|
+
getLocalGroupList(): Array<{
|
|
122
|
+
group_id: string;
|
|
123
|
+
name: string;
|
|
124
|
+
member_count?: number;
|
|
125
|
+
}>;
|
|
126
|
+
/**
|
|
127
|
+
* 添加群组到本地存储
|
|
128
|
+
*/
|
|
129
|
+
addGroupToStore(groupId: string, name: string): void;
|
|
130
|
+
/**
|
|
131
|
+
* 从本地存储删除群组
|
|
132
|
+
*/
|
|
133
|
+
removeGroupFromStore(groupId: string): Promise<void>;
|
|
134
|
+
/**
|
|
135
|
+
* 获取本地存储的群消息
|
|
136
|
+
*/
|
|
137
|
+
getLocalGroupMessages(groupId: string, limit?: number): GroupMessage[];
|
|
138
|
+
/**
|
|
139
|
+
* 添加群消息到本地存储
|
|
140
|
+
*/
|
|
141
|
+
addGroupMessageToStore(groupId: string, msg: GroupMessage): void;
|
|
142
|
+
/**
|
|
143
|
+
* 批量添加群消息到本地存储
|
|
144
|
+
*/
|
|
145
|
+
addGroupMessagesToStore(groupId: string, msgs: GroupMessage[]): void;
|
|
146
|
+
/**
|
|
147
|
+
* 获取本地存储中群组的最新消息ID(用于增量拉取)
|
|
148
|
+
*/
|
|
149
|
+
getGroupLastMsgId(groupId: string): number;
|
|
150
|
+
/**
|
|
151
|
+
* 从服务端拉取新消息并同步到本地存储。
|
|
152
|
+
* 返回所有本地缓存的消息(包括新拉取的)。
|
|
153
|
+
*/
|
|
154
|
+
pullAndStoreGroupMessages(groupId: string, limit?: number): Promise<GroupMessage[]>;
|
|
155
|
+
/**
|
|
156
|
+
* 关闭群消息存储,刷新所有未写入的数据
|
|
157
|
+
*/
|
|
158
|
+
closeGroupMessageStore(): Promise<void>;
|
|
46
159
|
private handleError;
|
|
47
160
|
}
|
|
48
161
|
export { AgentCP };
|
package/dist/agentcp.js
CHANGED
|
@@ -43,17 +43,25 @@ const utils_1 = require("./utils");
|
|
|
43
43
|
const messagestore_1 = require("./messagestore");
|
|
44
44
|
const filesync_1 = require("./filesync");
|
|
45
45
|
const agentmd_1 = require("./agentmd");
|
|
46
|
+
const group_1 = require("./group");
|
|
46
47
|
class AgentCP {
|
|
47
48
|
get messageStore() {
|
|
48
49
|
return this._messageStore;
|
|
49
50
|
}
|
|
50
51
|
constructor(apiUrl, seedPassword = "", basePath, options) {
|
|
51
|
-
var _a;
|
|
52
|
+
var _a, _b;
|
|
52
53
|
this.activeAid = '';
|
|
53
54
|
this.agentMdPath = null;
|
|
54
55
|
this.agentMdUploaded = false;
|
|
55
56
|
this.autoGenerateAgentMd = false;
|
|
56
57
|
this.agentMdOptions = {};
|
|
58
|
+
// Group client properties
|
|
59
|
+
this.groupClient = null;
|
|
60
|
+
this.groupOps = null;
|
|
61
|
+
this.groupMessageStore = null;
|
|
62
|
+
this._groupTargetAid = '';
|
|
63
|
+
this._groupSessionId = '';
|
|
64
|
+
this._persistGroupMessages = false;
|
|
57
65
|
if (!apiUrl) {
|
|
58
66
|
this.handleError("参数缺失:apiUrl不应为空");
|
|
59
67
|
}
|
|
@@ -65,11 +73,13 @@ class AgentCP {
|
|
|
65
73
|
}
|
|
66
74
|
const baseUrl = `https://acp3.${apiUrl}`;
|
|
67
75
|
this.seedPassword = seedPassword;
|
|
76
|
+
this._basePath = basePath || process.cwd();
|
|
68
77
|
this.apUrl = `${baseUrl}/api/accesspoint`;
|
|
69
78
|
this.msgUrl = `${baseUrl}/api/message`;
|
|
79
|
+
this._persistGroupMessages = (_a = options === null || options === void 0 ? void 0 : options.persistGroupMessages) !== null && _a !== void 0 ? _a : false;
|
|
70
80
|
this._messageStore = new messagestore_1.MessageStore({
|
|
71
|
-
persistMessages: (
|
|
72
|
-
basePath:
|
|
81
|
+
persistMessages: (_b = options === null || options === void 0 ? void 0 : options.persistMessages) !== null && _b !== void 0 ? _b : false,
|
|
82
|
+
basePath: this._basePath,
|
|
73
83
|
});
|
|
74
84
|
// 预热加密模块
|
|
75
85
|
this.initializeCrypto();
|
|
@@ -293,6 +303,285 @@ class AgentCP {
|
|
|
293
303
|
console.warn('保存 agent.md 标记文件失败:', err);
|
|
294
304
|
}
|
|
295
305
|
}
|
|
306
|
+
// ============================================================
|
|
307
|
+
// Group Client Integration
|
|
308
|
+
// ============================================================
|
|
309
|
+
/**
|
|
310
|
+
* 获取群组目标AID
|
|
311
|
+
*/
|
|
312
|
+
getGroupTargetAid() {
|
|
313
|
+
return this._groupTargetAid;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* 初始化群组客户端。
|
|
317
|
+
* 需要外部提供 sendRaw 函数和 sessionId,因为 AgentCP 本身不管理 WebSocket 连接。
|
|
318
|
+
*
|
|
319
|
+
* @param sendRaw 发送原始消息的函数: (message, to, sessionId) => void
|
|
320
|
+
* @param sessionId 与 group.{issuer} 的会话ID
|
|
321
|
+
* @param targetAid 目标群组AID,默认 "group.{issuer}"
|
|
322
|
+
*/
|
|
323
|
+
initGroupClient(sendRaw, sessionId, targetAid) {
|
|
324
|
+
if (!targetAid) {
|
|
325
|
+
const parts = this.activeAid.split(".", 1);
|
|
326
|
+
const issuer = this.activeAid.substring(parts[0].length + 1) || this.activeAid;
|
|
327
|
+
targetAid = `group.${issuer}`;
|
|
328
|
+
}
|
|
329
|
+
this._groupTargetAid = targetAid;
|
|
330
|
+
this._groupSessionId = sessionId;
|
|
331
|
+
const sendFunc = (target, payload) => {
|
|
332
|
+
sendRaw(payload, target, this._groupSessionId);
|
|
333
|
+
};
|
|
334
|
+
this.groupClient = new group_1.ACPGroupClient(this.activeAid, sendFunc);
|
|
335
|
+
this.groupOps = new group_1.GroupOperations(this.groupClient);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* 初始化跨AP群组客户端。
|
|
339
|
+
*
|
|
340
|
+
* @param sendRaw 发送原始消息的函数
|
|
341
|
+
* @param sessionId 与目标群组AID的会话ID
|
|
342
|
+
* @param targetAid 目标群组AID (如 "group.aid.com")
|
|
343
|
+
*/
|
|
344
|
+
initGroupClientCrossAp(sendRaw, sessionId, targetAid) {
|
|
345
|
+
this._groupTargetAid = targetAid;
|
|
346
|
+
this._groupSessionId = sessionId;
|
|
347
|
+
const sendFunc = (target, payload) => {
|
|
348
|
+
sendRaw(payload, target, this._groupSessionId);
|
|
349
|
+
};
|
|
350
|
+
this.groupClient = new group_1.ACPGroupClient(this.activeAid, sendFunc);
|
|
351
|
+
this.groupOps = new group_1.GroupOperations(this.groupClient);
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* 处理群组协议消息路由。
|
|
355
|
+
* 当收到 session_message 时,检查 sender 是否为群组目标AID,
|
|
356
|
+
* 如果是则路由到 groupClient.handleIncoming()。
|
|
357
|
+
*
|
|
358
|
+
* @returns true 如果消息被群组客户端处理
|
|
359
|
+
*/
|
|
360
|
+
handleGroupMessage(message) {
|
|
361
|
+
var _a, _b;
|
|
362
|
+
if (this.groupClient == null || !this._groupTargetAid) {
|
|
363
|
+
console.log(`[Group] handleGroupMessage skipped: groupClient=${this.groupClient != null}, targetAid=${this._groupTargetAid}`);
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
const data = message.data || message;
|
|
367
|
+
const sender = (_a = data.sender) !== null && _a !== void 0 ? _a : "";
|
|
368
|
+
// console.log(`[Group] handleGroupMessage: sender="${sender}" targetAid="${this._groupTargetAid}" match=${sender === this._groupTargetAid}`);
|
|
369
|
+
if (sender === this._groupTargetAid) {
|
|
370
|
+
try {
|
|
371
|
+
const rawMsg = (_b = data.message) !== null && _b !== void 0 ? _b : "";
|
|
372
|
+
// console.log(`[Group] rawMsg type=${typeof rawMsg}, length=${typeof rawMsg === 'string' ? rawMsg.length : 'N/A'}, first 300 chars: ${typeof rawMsg === 'string' ? rawMsg.substring(0, 300) : JSON.stringify(rawMsg).substring(0, 300)}`);
|
|
373
|
+
if (typeof rawMsg === 'string' && rawMsg) {
|
|
374
|
+
this.groupClient.handleIncoming(rawMsg);
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
console.warn(`[Group] rawMsg is not a non-empty string, skipping handleIncoming`);
|
|
378
|
+
}
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
catch (e) {
|
|
382
|
+
console.error("[Group] handleIncoming error:", e);
|
|
383
|
+
return true;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* 设置群组事件处理器
|
|
390
|
+
*/
|
|
391
|
+
setGroupEventHandler(handler) {
|
|
392
|
+
if (this.groupClient) {
|
|
393
|
+
this.groupClient.setEventHandler(handler);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* 设置群组游标存储
|
|
398
|
+
*/
|
|
399
|
+
setGroupCursorStore(store) {
|
|
400
|
+
if (this.groupClient) {
|
|
401
|
+
this.groupClient.setCursorStore(store);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* 关闭群组客户端
|
|
406
|
+
*/
|
|
407
|
+
closeGroupClient() {
|
|
408
|
+
if (this.groupClient) {
|
|
409
|
+
try {
|
|
410
|
+
this.groupClient.close();
|
|
411
|
+
}
|
|
412
|
+
catch (e) {
|
|
413
|
+
console.error("[Group] group_client close error:", e);
|
|
414
|
+
}
|
|
415
|
+
this.groupClient = null;
|
|
416
|
+
this.groupOps = null;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* 初始化群消息持久化存储
|
|
421
|
+
*/
|
|
422
|
+
async initGroupMessageStore(options) {
|
|
423
|
+
this.groupMessageStore = new group_1.GroupMessageStore({
|
|
424
|
+
persistMessages: this._persistGroupMessages,
|
|
425
|
+
basePath: this._basePath,
|
|
426
|
+
maxMessagesPerGroup: options === null || options === void 0 ? void 0 : options.maxMessagesPerGroup,
|
|
427
|
+
maxEventsPerGroup: options === null || options === void 0 ? void 0 : options.maxEventsPerGroup,
|
|
428
|
+
});
|
|
429
|
+
if (this.activeAid) {
|
|
430
|
+
try {
|
|
431
|
+
await this.groupMessageStore.loadGroupsForAid(this.activeAid);
|
|
432
|
+
}
|
|
433
|
+
catch (e) {
|
|
434
|
+
console.warn('[AgentCP] 加载群消息存储失败:', e);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* 确保群消息持久化存储已初始化并加载完成。
|
|
440
|
+
* 如果已初始化则直接返回,否则初始化并等待磁盘数据加载完成。
|
|
441
|
+
*/
|
|
442
|
+
async ensureGroupMessageStore() {
|
|
443
|
+
if (this.groupMessageStore)
|
|
444
|
+
return;
|
|
445
|
+
if (this._persistGroupMessages) {
|
|
446
|
+
await this.initGroupMessageStore();
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
// ============================================================
|
|
450
|
+
// Group Storage Management (CRUD API)
|
|
451
|
+
// ============================================================
|
|
452
|
+
/**
|
|
453
|
+
* 从服务端拉取群组列表并同步到本地存储。
|
|
454
|
+
* 返回群组列表(含 group_id, name, member_count 等)。
|
|
455
|
+
*/
|
|
456
|
+
async syncGroupList() {
|
|
457
|
+
if (!this.groupOps || !this._groupTargetAid) {
|
|
458
|
+
throw new Error('群组客户端未初始化,请先调用 initGroupClient');
|
|
459
|
+
}
|
|
460
|
+
const result = await this.groupOps.listMyGroups(this._groupTargetAid);
|
|
461
|
+
const groups = [];
|
|
462
|
+
for (const membership of result.groups) {
|
|
463
|
+
// 尝试获取群组详细信息
|
|
464
|
+
let name = membership.group_id;
|
|
465
|
+
let memberCount;
|
|
466
|
+
try {
|
|
467
|
+
const info = await this.groupOps.getGroupInfo(this._groupTargetAid, membership.group_id);
|
|
468
|
+
name = info.name || membership.group_id;
|
|
469
|
+
memberCount = info.member_count;
|
|
470
|
+
}
|
|
471
|
+
catch (_) { }
|
|
472
|
+
groups.push({ group_id: membership.group_id, name, member_count: memberCount });
|
|
473
|
+
// 同步到本地存储
|
|
474
|
+
if (this.groupMessageStore) {
|
|
475
|
+
this.groupMessageStore.getOrCreateGroup(membership.group_id, this._groupTargetAid, name);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return groups;
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* 获取本地存储的群组列表。
|
|
482
|
+
* 如果持久化存储已初始化,从存储中读取;否则返回空数组。
|
|
483
|
+
*/
|
|
484
|
+
getLocalGroupList() {
|
|
485
|
+
if (!this.groupMessageStore)
|
|
486
|
+
return [];
|
|
487
|
+
return this.groupMessageStore.getGroupList().map(r => ({
|
|
488
|
+
group_id: r.groupId,
|
|
489
|
+
name: r.groupName,
|
|
490
|
+
}));
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* 添加群组到本地存储
|
|
494
|
+
*/
|
|
495
|
+
addGroupToStore(groupId, name) {
|
|
496
|
+
if (!this.groupMessageStore)
|
|
497
|
+
return;
|
|
498
|
+
this.groupMessageStore.getOrCreateGroup(groupId, this._groupTargetAid, name);
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* 从本地存储删除群组
|
|
502
|
+
*/
|
|
503
|
+
async removeGroupFromStore(groupId) {
|
|
504
|
+
if (!this.groupMessageStore)
|
|
505
|
+
return;
|
|
506
|
+
await this.groupMessageStore.deleteGroup(groupId);
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* 获取本地存储的群消息
|
|
510
|
+
*/
|
|
511
|
+
getLocalGroupMessages(groupId, limit) {
|
|
512
|
+
if (!this.groupMessageStore)
|
|
513
|
+
return [];
|
|
514
|
+
if (limit) {
|
|
515
|
+
return this.groupMessageStore.getLatestMessages(groupId, limit);
|
|
516
|
+
}
|
|
517
|
+
return this.groupMessageStore.getMessages(groupId);
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* 添加群消息到本地存储
|
|
521
|
+
*/
|
|
522
|
+
addGroupMessageToStore(groupId, msg) {
|
|
523
|
+
if (!this.groupMessageStore)
|
|
524
|
+
return;
|
|
525
|
+
this.groupMessageStore.addMessage(groupId, msg);
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* 批量添加群消息到本地存储
|
|
529
|
+
*/
|
|
530
|
+
addGroupMessagesToStore(groupId, msgs) {
|
|
531
|
+
if (!this.groupMessageStore)
|
|
532
|
+
return;
|
|
533
|
+
this.groupMessageStore.addMessages(groupId, msgs);
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* 获取本地存储中群组的最新消息ID(用于增量拉取)
|
|
537
|
+
*/
|
|
538
|
+
getGroupLastMsgId(groupId) {
|
|
539
|
+
var _a;
|
|
540
|
+
if (!this.groupMessageStore)
|
|
541
|
+
return 0;
|
|
542
|
+
const record = this.groupMessageStore.getGroup(groupId);
|
|
543
|
+
return (_a = record === null || record === void 0 ? void 0 : record.lastMsgId) !== null && _a !== void 0 ? _a : 0;
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* 从服务端拉取新消息并同步到本地存储。
|
|
547
|
+
* 返回所有本地缓存的消息(包括新拉取的)。
|
|
548
|
+
*/
|
|
549
|
+
async pullAndStoreGroupMessages(groupId, limit = 50) {
|
|
550
|
+
if (!this.groupOps || !this._groupTargetAid) {
|
|
551
|
+
throw new Error('群组客户端未初始化');
|
|
552
|
+
}
|
|
553
|
+
const lastMsgId = this.getGroupLastMsgId(groupId);
|
|
554
|
+
try {
|
|
555
|
+
const pulled = await this.groupOps.pullMessages(this._groupTargetAid, groupId, lastMsgId, limit);
|
|
556
|
+
if (pulled.messages && pulled.messages.length > 0) {
|
|
557
|
+
const msgs = pulled.messages.map(m => {
|
|
558
|
+
var _a, _b, _c, _d, _e, _f;
|
|
559
|
+
return ({
|
|
560
|
+
msg_id: (_a = m.msg_id) !== null && _a !== void 0 ? _a : 0,
|
|
561
|
+
sender: (_b = m.sender) !== null && _b !== void 0 ? _b : '',
|
|
562
|
+
content: (_c = m.content) !== null && _c !== void 0 ? _c : '',
|
|
563
|
+
content_type: (_d = m.content_type) !== null && _d !== void 0 ? _d : 'text',
|
|
564
|
+
timestamp: (_e = m.timestamp) !== null && _e !== void 0 ? _e : 0,
|
|
565
|
+
metadata: (_f = m.metadata) !== null && _f !== void 0 ? _f : null,
|
|
566
|
+
});
|
|
567
|
+
});
|
|
568
|
+
this.addGroupMessagesToStore(groupId, msgs);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
catch (e) {
|
|
572
|
+
console.warn('[AgentCP] pullMessages error:', e.message);
|
|
573
|
+
}
|
|
574
|
+
return this.getLocalGroupMessages(groupId);
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* 关闭群消息存储,刷新所有未写入的数据
|
|
578
|
+
*/
|
|
579
|
+
async closeGroupMessageStore() {
|
|
580
|
+
if (this.groupMessageStore) {
|
|
581
|
+
await this.groupMessageStore.close();
|
|
582
|
+
this.groupMessageStore = null;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
296
585
|
handleError(error, customMessage) {
|
|
297
586
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
298
587
|
throw new Error(`${customMessage || '操作失败'}: ${errorMessage}`);
|
package/dist/agentws.d.ts
CHANGED
|
@@ -57,6 +57,19 @@ declare class AgentWS implements IAgentWS {
|
|
|
57
57
|
* @param identifyingCode 邀请码
|
|
58
58
|
*/
|
|
59
59
|
send(msg: string, to: string, sessionId: string, identifyingCode: string): void;
|
|
60
|
+
/**
|
|
61
|
+
* 发送原始消息(不做 URL 编码),用于群组协议等需要原始 JSON 的场景
|
|
62
|
+
* @param message 原始消息字符串
|
|
63
|
+
* @param to 接收者智能体ID
|
|
64
|
+
* @param sessionId 会话ID
|
|
65
|
+
* @returns true 发送成功,false 连接未建立
|
|
66
|
+
*/
|
|
67
|
+
sendRaw(message: string, to: string, sessionId: string): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* 设置原始消息拦截器。回调函数返回 true 表示已处理(拦截),不再继续分发。
|
|
70
|
+
* 用于群组协议消息路由。
|
|
71
|
+
*/
|
|
72
|
+
onRawMessage(cb: (message: any) => boolean): void;
|
|
60
73
|
/**
|
|
61
74
|
* 设置WebSocket连接状态变更的监听函数
|
|
62
75
|
* @param cb 状态变更回调函数
|
|
@@ -71,6 +84,10 @@ declare class AgentWS implements IAgentWS {
|
|
|
71
84
|
* 断开WebSocket连接
|
|
72
85
|
*/
|
|
73
86
|
disconnect(): void;
|
|
87
|
+
/**
|
|
88
|
+
* 重连 WebSocket(心跳重连成功后调用)
|
|
89
|
+
*/
|
|
90
|
+
reconnect(): Promise<void>;
|
|
74
91
|
/**
|
|
75
92
|
* 接受来自心跳通道的邀请
|
|
76
93
|
* 当心跳客户端收到邀请时,调用此方法通过 WebSocket 加入会话
|
package/dist/agentws.js
CHANGED
|
@@ -103,6 +103,23 @@ class AgentWS {
|
|
|
103
103
|
send(msg, to, sessionId, identifyingCode) {
|
|
104
104
|
this.msgClient.send(msg, to, sessionId, identifyingCode);
|
|
105
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* 发送原始消息(不做 URL 编码),用于群组协议等需要原始 JSON 的场景
|
|
108
|
+
* @param message 原始消息字符串
|
|
109
|
+
* @param to 接收者智能体ID
|
|
110
|
+
* @param sessionId 会话ID
|
|
111
|
+
* @returns true 发送成功,false 连接未建立
|
|
112
|
+
*/
|
|
113
|
+
sendRaw(message, to, sessionId) {
|
|
114
|
+
return this.msgClient.sendRaw(message, to, sessionId);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* 设置原始消息拦截器。回调函数返回 true 表示已处理(拦截),不再继续分发。
|
|
118
|
+
* 用于群组协议消息路由。
|
|
119
|
+
*/
|
|
120
|
+
onRawMessage(cb) {
|
|
121
|
+
this.msgClient.onRawMessage(cb);
|
|
122
|
+
}
|
|
106
123
|
/**
|
|
107
124
|
* 设置WebSocket连接状态变更的监听函数
|
|
108
125
|
* @param cb 状态变更回调函数
|
|
@@ -127,6 +144,12 @@ class AgentWS {
|
|
|
127
144
|
disconnect() {
|
|
128
145
|
this.msgClient.disconnect();
|
|
129
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* 重连 WebSocket(心跳重连成功后调用)
|
|
149
|
+
*/
|
|
150
|
+
async reconnect() {
|
|
151
|
+
await this.msgClient.reconnect();
|
|
152
|
+
}
|
|
130
153
|
/**
|
|
131
154
|
* 接受来自心跳通道的邀请
|
|
132
155
|
* 当心跳客户端收到邀请时,调用此方法通过 WebSocket 加入会话
|
package/dist/datamanager.js
CHANGED
|
@@ -163,7 +163,14 @@ class CertAndKeyStore {
|
|
|
163
163
|
return [];
|
|
164
164
|
}
|
|
165
165
|
const entries = fs.readdirSync(aidsDir, { withFileTypes: true });
|
|
166
|
-
return entries.filter(e =>
|
|
166
|
+
return entries.filter(e => {
|
|
167
|
+
if (!e.isDirectory())
|
|
168
|
+
return false;
|
|
169
|
+
const aidName = e.name;
|
|
170
|
+
const keyPath = path.join(aidsDir, aidName, 'private', `${aidName}.key`);
|
|
171
|
+
const crtPath = path.join(aidsDir, aidName, 'public', `${aidName}.crt`);
|
|
172
|
+
return fs.existsSync(keyPath) && fs.existsSync(crtPath);
|
|
173
|
+
}).map(e => e.name);
|
|
167
174
|
}
|
|
168
175
|
catch (e) {
|
|
169
176
|
console.error('扫描 AIDs 目录失败:', e);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ACP Group Client - core request/response transport.
|
|
3
|
+
* Mirrors Python SDK: agentcp/group/client.py
|
|
4
|
+
*
|
|
5
|
+
* Key difference: Python uses queue.Queue blocking; TypeScript uses Promise + Deferred pattern.
|
|
6
|
+
* JS single-threaded: no locks needed.
|
|
7
|
+
*/
|
|
8
|
+
import { GroupResponse } from './types';
|
|
9
|
+
import { ACPGroupEventHandler } from './events';
|
|
10
|
+
import { CursorStore } from './cursor_store';
|
|
11
|
+
/** Type alias: send_func(targetAid, payload) -> void */
|
|
12
|
+
export type SendFunc = (targetAid: string, payload: string) => void;
|
|
13
|
+
export declare class ACPGroupClient {
|
|
14
|
+
private _agentId;
|
|
15
|
+
private _sendFunc;
|
|
16
|
+
private _pendingReqs;
|
|
17
|
+
private _handler;
|
|
18
|
+
private _cursorStore;
|
|
19
|
+
private _reqTimeout;
|
|
20
|
+
private _seqId;
|
|
21
|
+
constructor(agentId: string, sendFunc: SendFunc);
|
|
22
|
+
setEventHandler(handler: ACPGroupEventHandler): void;
|
|
23
|
+
setCursorStore(store: CursorStore): void;
|
|
24
|
+
getCursorStore(): CursorStore | null;
|
|
25
|
+
setTimeout(timeout: number): void;
|
|
26
|
+
private _nextRequestId;
|
|
27
|
+
/**
|
|
28
|
+
* Send a request and wait for response (Promise-based).
|
|
29
|
+
* Rejects with TimeoutError on timeout, Error on send failure.
|
|
30
|
+
*/
|
|
31
|
+
sendRequest(targetAid: string, groupId: string, action: string, params?: Record<string, any> | null, timeout?: number): Promise<GroupResponse>;
|
|
32
|
+
/**
|
|
33
|
+
* Handle an incoming ACP message (response or notification).
|
|
34
|
+
* Called by the message dispatch chain in AgentCP.
|
|
35
|
+
*/
|
|
36
|
+
handleIncoming(payload: string): void;
|
|
37
|
+
/**
|
|
38
|
+
* Close client, cancel all pending requests.
|
|
39
|
+
*/
|
|
40
|
+
close(): void;
|
|
41
|
+
}
|