acp-ts 1.1.6 → 1.1.8
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/agentcp.d.ts +63 -4
- package/dist/agentcp.js +192 -11
- package/dist/group/client.js +30 -5
- package/dist/group/index.d.ts +1 -1
- package/dist/group/operations.d.ts +23 -2
- package/dist/group/operations.js +43 -3
- package/dist/group/types.d.ts +3 -0
- package/dist/server.js +406 -49
- package/dist/websocket.js +0 -5
- package/package.json +1 -1
package/dist/agentcp.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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
|
+
import { ACPGroupClient, GroupOperations, ACPGroupEventHandler, CursorStore, GroupMessageStore, GroupMessage, MsgCursor } from "./group";
|
|
5
5
|
declare class AgentCP implements IAgentCP {
|
|
6
6
|
private seedPassword;
|
|
7
7
|
private apUrl;
|
|
@@ -19,6 +19,9 @@ declare class AgentCP implements IAgentCP {
|
|
|
19
19
|
private _groupTargetAid;
|
|
20
20
|
private _groupSessionId;
|
|
21
21
|
private _persistGroupMessages;
|
|
22
|
+
private _onlineGroups;
|
|
23
|
+
private _heartbeatTimer;
|
|
24
|
+
private _heartbeatIntervalMs;
|
|
22
25
|
get messageStore(): MessageStore;
|
|
23
26
|
constructor(apiUrl: string, seedPassword?: string, basePath?: string, options?: {
|
|
24
27
|
persistMessages?: boolean;
|
|
@@ -86,16 +89,23 @@ declare class AgentCP implements IAgentCP {
|
|
|
86
89
|
*/
|
|
87
90
|
setGroupEventHandler(handler: ACPGroupEventHandler): void;
|
|
88
91
|
/**
|
|
89
|
-
*
|
|
92
|
+
* 创建默认的群组事件处理器。
|
|
93
|
+
* - onGroupMessage: 自动存储消息到 GroupMessageStore + 自动 ACK
|
|
94
|
+
* - onJoinApproved: 自动注册到 Home AP + 存储群组
|
|
90
95
|
* 外部可通过 setGroupEventHandler 覆盖。
|
|
91
96
|
*/
|
|
92
97
|
private _createDefaultGroupEventHandler;
|
|
98
|
+
/**
|
|
99
|
+
* 自动 ACK 消息(SDK 内部行为,对上层透明)
|
|
100
|
+
*/
|
|
101
|
+
private _autoAckMessage;
|
|
93
102
|
/**
|
|
94
103
|
* 设置群组游标存储
|
|
95
104
|
*/
|
|
96
105
|
setGroupCursorStore(store: CursorStore): void;
|
|
97
106
|
/**
|
|
98
|
-
*
|
|
107
|
+
* 关闭群组客户端。
|
|
108
|
+
* 自动停止心跳、清理在线群组状态。
|
|
99
109
|
*/
|
|
100
110
|
closeGroupClient(): void;
|
|
101
111
|
/**
|
|
@@ -132,6 +142,10 @@ declare class AgentCP implements IAgentCP {
|
|
|
132
142
|
* 添加群组到本地存储
|
|
133
143
|
*/
|
|
134
144
|
addGroupToStore(groupId: string, name: string): void;
|
|
145
|
+
/**
|
|
146
|
+
* 将加入的群组注册到 Home AP(内部方法)
|
|
147
|
+
*/
|
|
148
|
+
registerGroupToHomeAP(groupId: string, groupUrl: string, role?: string): Promise<void>;
|
|
135
149
|
/**
|
|
136
150
|
* 从本地存储删除群组
|
|
137
151
|
*/
|
|
@@ -154,9 +168,54 @@ declare class AgentCP implements IAgentCP {
|
|
|
154
168
|
getGroupLastMsgId(groupId: string): number;
|
|
155
169
|
/**
|
|
156
170
|
* 从服务端拉取新消息并同步到本地存储。
|
|
171
|
+
* 循环拉取直到 has_more=false,每批拉取后自动 ACK。
|
|
157
172
|
* 返回所有本地缓存的消息(包括新拉取的)。
|
|
173
|
+
*
|
|
174
|
+
* @param groupId 群组 ID
|
|
175
|
+
* @param afterMsgId 从哪条消息之后开始拉取,0 表示使用服务端自动游标模式
|
|
176
|
+
* @param limit 每次拉取数量上限,0 表示使用服务端默认值
|
|
177
|
+
*/
|
|
178
|
+
pullAndStoreGroupMessages(groupId: string, afterMsgId?: number, limit?: number): Promise<GroupMessage[]>;
|
|
179
|
+
/**
|
|
180
|
+
* 加入群组会话(完整生命周期):
|
|
181
|
+
* 1. register_online → 获取游标状态
|
|
182
|
+
* 2. 如果有未读消息 → 循环 pull_messages + ACK
|
|
183
|
+
* 3. 启动心跳定时器(首次时启动)
|
|
184
|
+
*
|
|
185
|
+
* @returns msg_cursor 游标状态
|
|
186
|
+
*/
|
|
187
|
+
joinGroupSession(groupId: string): Promise<MsgCursor>;
|
|
188
|
+
/**
|
|
189
|
+
* 离开群组会话(优雅退出):
|
|
190
|
+
* 1. unregister_online
|
|
191
|
+
* 2. 从在线群组列表移除
|
|
192
|
+
* 3. 如果没有在线群组了,停止心跳定时器
|
|
193
|
+
*/
|
|
194
|
+
leaveGroupSession(groupId: string): Promise<void>;
|
|
195
|
+
/**
|
|
196
|
+
* 离开所有群组会话
|
|
197
|
+
*/
|
|
198
|
+
leaveAllGroupSessions(): Promise<void>;
|
|
199
|
+
/**
|
|
200
|
+
* 获取当前在线群组列表
|
|
201
|
+
*/
|
|
202
|
+
getOnlineGroups(): string[];
|
|
203
|
+
/**
|
|
204
|
+
* 设置心跳间隔(毫秒),默认 180000(3 分钟)
|
|
205
|
+
*/
|
|
206
|
+
setHeartbeatInterval(intervalMs: number): void;
|
|
207
|
+
/**
|
|
208
|
+
* 确保心跳定时器已启动
|
|
209
|
+
*/
|
|
210
|
+
private _ensureHeartbeat;
|
|
211
|
+
/**
|
|
212
|
+
* 停止心跳定时器
|
|
213
|
+
*/
|
|
214
|
+
private _stopHeartbeat;
|
|
215
|
+
/**
|
|
216
|
+
* 对所有在线群组发送心跳
|
|
158
217
|
*/
|
|
159
|
-
|
|
218
|
+
private _sendHeartbeats;
|
|
160
219
|
/**
|
|
161
220
|
* 关闭群消息存储,刷新所有未写入的数据
|
|
162
221
|
*/
|
package/dist/agentcp.js
CHANGED
|
@@ -62,6 +62,10 @@ class AgentCP {
|
|
|
62
62
|
this._groupTargetAid = '';
|
|
63
63
|
this._groupSessionId = '';
|
|
64
64
|
this._persistGroupMessages = false;
|
|
65
|
+
// Group lifecycle management
|
|
66
|
+
this._onlineGroups = new Set();
|
|
67
|
+
this._heartbeatTimer = null;
|
|
68
|
+
this._heartbeatIntervalMs = 180000; // 3 minutes
|
|
65
69
|
if (!apiUrl) {
|
|
66
70
|
this.handleError("参数缺失:apiUrl不应为空");
|
|
67
71
|
}
|
|
@@ -369,11 +373,9 @@ class AgentCP {
|
|
|
369
373
|
}
|
|
370
374
|
const data = message.data || message;
|
|
371
375
|
const sender = (_a = data.sender) !== null && _a !== void 0 ? _a : "";
|
|
372
|
-
// console.log(`[Group] handleGroupMessage: sender="${sender}" targetAid="${this._groupTargetAid}" match=${sender === this._groupTargetAid}`);
|
|
373
376
|
if (sender === this._groupTargetAid) {
|
|
374
377
|
try {
|
|
375
378
|
const rawMsg = (_b = data.message) !== null && _b !== void 0 ? _b : "";
|
|
376
|
-
// 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)}`);
|
|
377
379
|
if (typeof rawMsg === 'string' && rawMsg) {
|
|
378
380
|
this.groupClient.handleIncoming(rawMsg);
|
|
379
381
|
}
|
|
@@ -398,7 +400,9 @@ class AgentCP {
|
|
|
398
400
|
}
|
|
399
401
|
}
|
|
400
402
|
/**
|
|
401
|
-
*
|
|
403
|
+
* 创建默认的群组事件处理器。
|
|
404
|
+
* - onGroupMessage: 自动存储消息到 GroupMessageStore + 自动 ACK
|
|
405
|
+
* - onJoinApproved: 自动注册到 Home AP + 存储群组
|
|
402
406
|
* 外部可通过 setGroupEventHandler 覆盖。
|
|
403
407
|
*/
|
|
404
408
|
_createDefaultGroupEventHandler() {
|
|
@@ -412,8 +416,28 @@ class AgentCP {
|
|
|
412
416
|
onGroupInvite(groupId, groupAddress, invitedBy) {
|
|
413
417
|
console.log(`[Group][DefaultHandler] onGroupInvite: group=${groupId} address=${groupAddress} invitedBy=${invitedBy}`);
|
|
414
418
|
},
|
|
415
|
-
onJoinApproved(groupId, groupAddress) {
|
|
419
|
+
onJoinApproved: (groupId, groupAddress) => {
|
|
416
420
|
console.log(`[Group][DefaultHandler] onJoinApproved: group=${groupId} address=${groupAddress}`);
|
|
421
|
+
(async () => {
|
|
422
|
+
try {
|
|
423
|
+
if (!this.groupOps || !this._groupTargetAid) {
|
|
424
|
+
console.warn(`[Group][DefaultHandler] onJoinApproved skipped: groupOps or targetAid not available`);
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
let groupName = groupId;
|
|
428
|
+
try {
|
|
429
|
+
const info = await this.groupOps.getGroupInfo(this._groupTargetAid, groupId);
|
|
430
|
+
groupName = info.name || groupId;
|
|
431
|
+
}
|
|
432
|
+
catch (_) { }
|
|
433
|
+
this.addGroupToStore(groupId, groupName);
|
|
434
|
+
const groupUrl = groupAddress || `https://${this._groupTargetAid}/${groupId}`;
|
|
435
|
+
await this.registerGroupToHomeAP(groupId, groupUrl);
|
|
436
|
+
}
|
|
437
|
+
catch (e) {
|
|
438
|
+
console.error(`[Group][DefaultHandler] onJoinApproved processing failed: group=${groupId}`, e.message);
|
|
439
|
+
}
|
|
440
|
+
})();
|
|
417
441
|
},
|
|
418
442
|
onJoinRejected(groupId, reason) {
|
|
419
443
|
console.log(`[Group][DefaultHandler] onJoinRejected: group=${groupId} reason=${reason}`);
|
|
@@ -421,14 +445,28 @@ class AgentCP {
|
|
|
421
445
|
onJoinRequestReceived(groupId, agentId, message) {
|
|
422
446
|
console.log(`[Group][DefaultHandler] onJoinRequestReceived: group=${groupId} agent=${agentId}`);
|
|
423
447
|
},
|
|
424
|
-
onGroupMessage(groupId, msg) {
|
|
448
|
+
onGroupMessage: (groupId, msg) => {
|
|
425
449
|
console.log(`[Group][DefaultHandler] onGroupMessage: group=${groupId} msgId=${msg.msg_id} sender=${msg.sender}`);
|
|
450
|
+
// 自动存储消息到本地
|
|
451
|
+
this.addGroupMessageToStore(groupId, msg);
|
|
452
|
+
// 自动 ACK(异步,不阻塞回调)
|
|
453
|
+
this._autoAckMessage(groupId, msg.msg_id);
|
|
426
454
|
},
|
|
427
455
|
onGroupEvent(groupId, evt) {
|
|
428
456
|
console.log(`[Group][DefaultHandler] onGroupEvent: group=${groupId} event=${evt.event_type}`);
|
|
429
457
|
},
|
|
430
458
|
};
|
|
431
459
|
}
|
|
460
|
+
/**
|
|
461
|
+
* 自动 ACK 消息(SDK 内部行为,对上层透明)
|
|
462
|
+
*/
|
|
463
|
+
_autoAckMessage(groupId, msgId) {
|
|
464
|
+
if (!this.groupOps || !this._groupTargetAid)
|
|
465
|
+
return;
|
|
466
|
+
this.groupOps.ackMessages(this._groupTargetAid, groupId, msgId).catch(e => {
|
|
467
|
+
console.warn(`[Group] auto ack failed: group=${groupId} msgId=${msgId}`, e.message || e);
|
|
468
|
+
});
|
|
469
|
+
}
|
|
432
470
|
/**
|
|
433
471
|
* 设置群组游标存储
|
|
434
472
|
*/
|
|
@@ -438,9 +476,12 @@ class AgentCP {
|
|
|
438
476
|
}
|
|
439
477
|
}
|
|
440
478
|
/**
|
|
441
|
-
*
|
|
479
|
+
* 关闭群组客户端。
|
|
480
|
+
* 自动停止心跳、清理在线群组状态。
|
|
442
481
|
*/
|
|
443
482
|
closeGroupClient() {
|
|
483
|
+
this._stopHeartbeat();
|
|
484
|
+
this._onlineGroups.clear();
|
|
444
485
|
if (this.groupClient) {
|
|
445
486
|
try {
|
|
446
487
|
this.groupClient.close();
|
|
@@ -533,6 +574,20 @@ class AgentCP {
|
|
|
533
574
|
return;
|
|
534
575
|
this.groupMessageStore.getOrCreateGroup(groupId, this._groupTargetAid, name);
|
|
535
576
|
}
|
|
577
|
+
/**
|
|
578
|
+
* 将加入的群组注册到 Home AP(内部方法)
|
|
579
|
+
*/
|
|
580
|
+
async registerGroupToHomeAP(groupId, groupUrl, role = 'member') {
|
|
581
|
+
if (!this.groupOps || !this._groupTargetAid)
|
|
582
|
+
return;
|
|
583
|
+
try {
|
|
584
|
+
await this.groupOps.registerMembership(this._groupTargetAid, groupId, groupUrl, this._groupTargetAid, this._groupSessionId, role);
|
|
585
|
+
console.log(`[Group] registerGroupToHomeAP success: group=${groupId}`);
|
|
586
|
+
}
|
|
587
|
+
catch (e) {
|
|
588
|
+
console.error(`[Group] registerGroupToHomeAP failed: group=${groupId}`, e.message);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
536
591
|
/**
|
|
537
592
|
* 从本地存储删除群组
|
|
538
593
|
*/
|
|
@@ -580,16 +635,24 @@ class AgentCP {
|
|
|
580
635
|
}
|
|
581
636
|
/**
|
|
582
637
|
* 从服务端拉取新消息并同步到本地存储。
|
|
638
|
+
* 循环拉取直到 has_more=false,每批拉取后自动 ACK。
|
|
583
639
|
* 返回所有本地缓存的消息(包括新拉取的)。
|
|
640
|
+
*
|
|
641
|
+
* @param groupId 群组 ID
|
|
642
|
+
* @param afterMsgId 从哪条消息之后开始拉取,0 表示使用服务端自动游标模式
|
|
643
|
+
* @param limit 每次拉取数量上限,0 表示使用服务端默认值
|
|
584
644
|
*/
|
|
585
|
-
async pullAndStoreGroupMessages(groupId, limit = 50) {
|
|
645
|
+
async pullAndStoreGroupMessages(groupId, afterMsgId = 0, limit = 50) {
|
|
586
646
|
if (!this.groupOps || !this._groupTargetAid) {
|
|
587
647
|
throw new Error('群组客户端未初始化');
|
|
588
648
|
}
|
|
589
|
-
|
|
649
|
+
let after = afterMsgId;
|
|
590
650
|
try {
|
|
591
|
-
|
|
592
|
-
|
|
651
|
+
while (true) {
|
|
652
|
+
const pulled = await this.groupOps.pullMessages(this._groupTargetAid, groupId, after, limit);
|
|
653
|
+
if (!pulled.messages || pulled.messages.length === 0) {
|
|
654
|
+
break;
|
|
655
|
+
}
|
|
593
656
|
const msgs = pulled.messages.map(m => {
|
|
594
657
|
var _a, _b, _c, _d, _e, _f;
|
|
595
658
|
return ({
|
|
@@ -602,13 +665,131 @@ class AgentCP {
|
|
|
602
665
|
});
|
|
603
666
|
});
|
|
604
667
|
this.addGroupMessagesToStore(groupId, msgs);
|
|
668
|
+
// ACK 这批消息中的最后一条
|
|
669
|
+
const lastMsgId = msgs[msgs.length - 1].msg_id;
|
|
670
|
+
await this.groupOps.ackMessages(this._groupTargetAid, groupId, lastMsgId);
|
|
671
|
+
// 更新 after 用于下一轮拉取
|
|
672
|
+
after = lastMsgId;
|
|
673
|
+
if (!pulled.has_more) {
|
|
674
|
+
break;
|
|
675
|
+
}
|
|
605
676
|
}
|
|
606
677
|
}
|
|
607
678
|
catch (e) {
|
|
608
|
-
console.warn('[AgentCP]
|
|
679
|
+
console.warn('[AgentCP] pullAndStoreGroupMessages error:', e.message);
|
|
609
680
|
}
|
|
610
681
|
return this.getLocalGroupMessages(groupId);
|
|
611
682
|
}
|
|
683
|
+
// ============================================================
|
|
684
|
+
// Group Session Lifecycle (register_online / pull / heartbeat / unregister)
|
|
685
|
+
// ============================================================
|
|
686
|
+
/**
|
|
687
|
+
* 加入群组会话(完整生命周期):
|
|
688
|
+
* 1. register_online → 获取游标状态
|
|
689
|
+
* 2. 如果有未读消息 → 循环 pull_messages + ACK
|
|
690
|
+
* 3. 启动心跳定时器(首次时启动)
|
|
691
|
+
*
|
|
692
|
+
* @returns msg_cursor 游标状态
|
|
693
|
+
*/
|
|
694
|
+
async joinGroupSession(groupId) {
|
|
695
|
+
if (!this.groupOps || !this._groupTargetAid) {
|
|
696
|
+
throw new Error('群组客户端未初始化,请先调用 initGroupClient');
|
|
697
|
+
}
|
|
698
|
+
// Step 1: register_online
|
|
699
|
+
const result = await this.groupOps.registerOnline(this._groupTargetAid, groupId);
|
|
700
|
+
const cursor = result.msg_cursor;
|
|
701
|
+
this._onlineGroups.add(groupId);
|
|
702
|
+
console.log(`[Group] joinGroupSession: group=${groupId} unread=${cursor.unread_count} current=${cursor.current_msg_id} latest=${cursor.latest_msg_id}`);
|
|
703
|
+
// Step 2: 如果有未读消息,使用自动游标模式拉取
|
|
704
|
+
if (cursor.unread_count > 0) {
|
|
705
|
+
await this.pullAndStoreGroupMessages(groupId, 0, 0);
|
|
706
|
+
}
|
|
707
|
+
// Step 3: 启动心跳定时器(首次加入群组时启动)
|
|
708
|
+
this._ensureHeartbeat();
|
|
709
|
+
return cursor;
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* 离开群组会话(优雅退出):
|
|
713
|
+
* 1. unregister_online
|
|
714
|
+
* 2. 从在线群组列表移除
|
|
715
|
+
* 3. 如果没有在线群组了,停止心跳定时器
|
|
716
|
+
*/
|
|
717
|
+
async leaveGroupSession(groupId) {
|
|
718
|
+
if (!this.groupOps || !this._groupTargetAid)
|
|
719
|
+
return;
|
|
720
|
+
try {
|
|
721
|
+
await this.groupOps.unregisterOnline(this._groupTargetAid, groupId);
|
|
722
|
+
}
|
|
723
|
+
catch (e) {
|
|
724
|
+
console.warn(`[Group] unregisterOnline failed: group=${groupId}`, e.message || e);
|
|
725
|
+
}
|
|
726
|
+
this._onlineGroups.delete(groupId);
|
|
727
|
+
// 如果没有在线群组了,停止心跳
|
|
728
|
+
if (this._onlineGroups.size === 0) {
|
|
729
|
+
this._stopHeartbeat();
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* 离开所有群组会话
|
|
734
|
+
*/
|
|
735
|
+
async leaveAllGroupSessions() {
|
|
736
|
+
const groups = Array.from(this._onlineGroups);
|
|
737
|
+
for (const groupId of groups) {
|
|
738
|
+
await this.leaveGroupSession(groupId);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* 获取当前在线群组列表
|
|
743
|
+
*/
|
|
744
|
+
getOnlineGroups() {
|
|
745
|
+
return Array.from(this._onlineGroups);
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* 设置心跳间隔(毫秒),默认 180000(3 分钟)
|
|
749
|
+
*/
|
|
750
|
+
setHeartbeatInterval(intervalMs) {
|
|
751
|
+
this._heartbeatIntervalMs = intervalMs;
|
|
752
|
+
// 如果心跳已在运行,重新启动以应用新间隔
|
|
753
|
+
if (this._heartbeatTimer) {
|
|
754
|
+
this._stopHeartbeat();
|
|
755
|
+
this._ensureHeartbeat();
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* 确保心跳定时器已启动
|
|
760
|
+
*/
|
|
761
|
+
_ensureHeartbeat() {
|
|
762
|
+
if (this._heartbeatTimer)
|
|
763
|
+
return;
|
|
764
|
+
if (this._onlineGroups.size === 0)
|
|
765
|
+
return;
|
|
766
|
+
this._heartbeatTimer = setInterval(() => {
|
|
767
|
+
this._sendHeartbeats();
|
|
768
|
+
}, this._heartbeatIntervalMs);
|
|
769
|
+
console.log(`[Group] heartbeat started: interval=${this._heartbeatIntervalMs}ms`);
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* 停止心跳定时器
|
|
773
|
+
*/
|
|
774
|
+
_stopHeartbeat() {
|
|
775
|
+
if (this._heartbeatTimer) {
|
|
776
|
+
clearInterval(this._heartbeatTimer);
|
|
777
|
+
this._heartbeatTimer = null;
|
|
778
|
+
console.log(`[Group] heartbeat stopped`);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* 对所有在线群组发送心跳
|
|
783
|
+
*/
|
|
784
|
+
_sendHeartbeats() {
|
|
785
|
+
if (!this.groupOps || !this._groupTargetAid)
|
|
786
|
+
return;
|
|
787
|
+
for (const groupId of this._onlineGroups) {
|
|
788
|
+
this.groupOps.heartbeat(this._groupTargetAid, groupId).catch(e => {
|
|
789
|
+
console.warn(`[Group] heartbeat failed: group=${groupId}`, e.message || e);
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
}
|
|
612
793
|
/**
|
|
613
794
|
* 关闭群消息存储,刷新所有未写入的数据
|
|
614
795
|
*/
|
package/dist/group/client.js
CHANGED
|
@@ -74,24 +74,21 @@ class ACPGroupClient {
|
|
|
74
74
|
* Called by the message dispatch chain in AgentCP.
|
|
75
75
|
*/
|
|
76
76
|
handleIncoming(payload) {
|
|
77
|
-
var _a, _b, _c;
|
|
78
|
-
// console.log(`[GroupClient] <<< handleIncoming raw payload (first 500 chars): ${payload.substring(0, 500)}`);
|
|
77
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
79
78
|
let data;
|
|
80
79
|
try {
|
|
81
80
|
data = JSON.parse(payload);
|
|
82
81
|
}
|
|
83
82
|
catch (e) {
|
|
84
|
-
console.error(`[GroupClient]
|
|
83
|
+
console.error(`[GroupClient] JSON.parse failed for incoming payload:`, e);
|
|
85
84
|
return;
|
|
86
85
|
}
|
|
87
|
-
// console.log(`[GroupClient] <<< parsed data: action=${data.action} request_id=${data.request_id} event=${data.event} code=${data.code}`);
|
|
88
86
|
// Try as response (has request_id)
|
|
89
87
|
const requestId = (_a = data.request_id) !== null && _a !== void 0 ? _a : "";
|
|
90
88
|
if (requestId) {
|
|
91
89
|
const resp = (0, types_1.parseGroupResponse)(data);
|
|
92
90
|
const pending = this._pendingReqs.get(requestId);
|
|
93
91
|
if (pending) {
|
|
94
|
-
// console.log(`[GroupClient] <<< matched pending request: reqId=${requestId}, resolving`);
|
|
95
92
|
clearTimeout(pending.timer);
|
|
96
93
|
this._pendingReqs.delete(requestId);
|
|
97
94
|
pending.resolve(resp);
|
|
@@ -119,6 +116,34 @@ class ACPGroupClient {
|
|
|
119
116
|
}
|
|
120
117
|
return;
|
|
121
118
|
}
|
|
119
|
+
// Handle action-based push messages from group.ap (e.g. message_push)
|
|
120
|
+
// These have action field but no event/request_id, need to be mapped to notification events
|
|
121
|
+
const action = (_d = data.action) !== null && _d !== void 0 ? _d : "";
|
|
122
|
+
if (action === "message_push" && data.data) {
|
|
123
|
+
console.log(`[GroupClient] message_push -> group_message: group=${data.group_id} msg_id=${data.data.msg_id}`);
|
|
124
|
+
const msgData = data.data;
|
|
125
|
+
const notify = {
|
|
126
|
+
action: "group_notify",
|
|
127
|
+
group_id: (_e = data.group_id) !== null && _e !== void 0 ? _e : "",
|
|
128
|
+
event: types_1.NOTIFY_GROUP_MESSAGE,
|
|
129
|
+
data: {
|
|
130
|
+
msg_id: (_f = msgData.msg_id) !== null && _f !== void 0 ? _f : 0,
|
|
131
|
+
sender: (_g = msgData.sender) !== null && _g !== void 0 ? _g : "",
|
|
132
|
+
content: (_h = msgData.content) !== null && _h !== void 0 ? _h : "",
|
|
133
|
+
content_type: (_j = msgData.content_type) !== null && _j !== void 0 ? _j : "text",
|
|
134
|
+
timestamp: (_k = msgData.timestamp) !== null && _k !== void 0 ? _k : 0,
|
|
135
|
+
metadata: (_l = msgData.metadata) !== null && _l !== void 0 ? _l : null,
|
|
136
|
+
},
|
|
137
|
+
timestamp: (_m = msgData.timestamp) !== null && _m !== void 0 ? _m : 0,
|
|
138
|
+
};
|
|
139
|
+
if (this._handler != null) {
|
|
140
|
+
(0, events_1.dispatchAcpNotify)(this._handler, notify);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
console.warn(`[GroupClient] !!! message_push dropped: no event handler registered.`);
|
|
144
|
+
}
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
122
147
|
console.warn(`[GroupClient] !!! unhandled incoming message: no request_id and no event field`, JSON.stringify(data).substring(0, 300));
|
|
123
148
|
}
|
|
124
149
|
// -- Lifecycle --
|
package/dist/group/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* ACP Group Operations package.
|
|
3
3
|
* Mirrors Python SDK: agentcp/group/__init__.py
|
|
4
4
|
*/
|
|
5
|
-
export { GroupErrorCode, GroupError, GroupRequest, GroupResponse, GroupNotify, buildGroupRequest, groupRequestToJson, parseGroupResponse, parseGroupNotify, GroupMessage, GroupEvent, MsgCursor, EventCursor, CursorState, createMsgCursor, createEventCursor, CreateGroupResp, SendMessageResp, PullMessagesResp, PullEventsResp, GroupInfoResp, BanlistResp, BatchReviewResp, PendingRequestsResp, MembersResp, AdminsResp, RulesResp, AnnouncementResp, JoinRequirementsResp, MasterResp, InviteCodeResp, InviteCodeListResp, BroadcastLockResp, BroadcastPermissionResp, SyncStatusResp, SyncLogResp, ChecksumResp, PublicGroupInfoResp, SearchGroupsResp, DigestResp, MembershipInfo, ListMyGroupsResp, GetFileResp, GetSummaryResp, GetMetricsResp, NOTIFY_NEW_MESSAGE, NOTIFY_NEW_EVENT, NOTIFY_GROUP_INVITE, NOTIFY_JOIN_APPROVED, NOTIFY_JOIN_REJECTED, NOTIFY_JOIN_REQUEST_RECEIVED, NOTIFY_GROUP_MESSAGE, NOTIFY_GROUP_EVENT, EVENT_MEMBER_JOINED, EVENT_MEMBER_REMOVED, EVENT_MEMBER_LEFT, EVENT_MEMBER_BANNED, EVENT_META_UPDATED, EVENT_RULES_UPDATED, EVENT_ANNOUNCEMENT_UPDATED, EVENT_GROUP_DISSOLVED, EVENT_MASTER_TRANSFERRED, EVENT_GROUP_SUSPENDED, EVENT_GROUP_RESUMED, EVENT_MEMBER_UNBANNED, EVENT_JOIN_REQUIREMENTS_UPDATED, EVENT_INVITE_CODE_CREATED, EVENT_INVITE_CODE_REVOKED, } from './types';
|
|
5
|
+
export { GroupErrorCode, GroupError, GroupRequest, GroupResponse, GroupNotify, buildGroupRequest, groupRequestToJson, parseGroupResponse, parseGroupNotify, GroupMessage, GroupEvent, MsgCursor, EventCursor, CursorState, createMsgCursor, createEventCursor, RegisterOnlineResp, CreateGroupResp, SendMessageResp, PullMessagesResp, PullEventsResp, GroupInfoResp, BanlistResp, BatchReviewResp, PendingRequestsResp, MembersResp, AdminsResp, RulesResp, AnnouncementResp, JoinRequirementsResp, MasterResp, InviteCodeResp, InviteCodeListResp, BroadcastLockResp, BroadcastPermissionResp, SyncStatusResp, SyncLogResp, ChecksumResp, PublicGroupInfoResp, SearchGroupsResp, DigestResp, MembershipInfo, ListMyGroupsResp, GetFileResp, GetSummaryResp, GetMetricsResp, NOTIFY_NEW_MESSAGE, NOTIFY_NEW_EVENT, NOTIFY_GROUP_INVITE, NOTIFY_JOIN_APPROVED, NOTIFY_JOIN_REJECTED, NOTIFY_JOIN_REQUEST_RECEIVED, NOTIFY_GROUP_MESSAGE, NOTIFY_GROUP_EVENT, EVENT_MEMBER_JOINED, EVENT_MEMBER_REMOVED, EVENT_MEMBER_LEFT, EVENT_MEMBER_BANNED, EVENT_META_UPDATED, EVENT_RULES_UPDATED, EVENT_ANNOUNCEMENT_UPDATED, EVENT_GROUP_DISSOLVED, EVENT_MASTER_TRANSFERRED, EVENT_GROUP_SUSPENDED, EVENT_GROUP_RESUMED, EVENT_MEMBER_UNBANNED, EVENT_JOIN_REQUIREMENTS_UPDATED, EVENT_INVITE_CODE_CREATED, EVENT_INVITE_CODE_REVOKED, } from './types';
|
|
6
6
|
export { ACPGroupClient, SendFunc } from './client';
|
|
7
7
|
export { GroupOperations, SyncHandler } from './operations';
|
|
8
8
|
export { ACPGroupEventHandler, EventProcessor, dispatchAcpNotify, dispatchEvent, } from './events';
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Mirrors Python SDK: agentcp/group/operations.py
|
|
4
4
|
*/
|
|
5
5
|
import { ACPGroupClient } from './client';
|
|
6
|
-
import { CreateGroupResp, SendMessageResp, PullMessagesResp, PullEventsResp, CursorState, GroupInfoResp, BanlistResp, BatchReviewResp, PendingRequestsResp, MembersResp, AdminsResp, RulesResp, AnnouncementResp, JoinRequirementsResp, MasterResp, InviteCodeResp, InviteCodeListResp, BroadcastLockResp, BroadcastPermissionResp, SyncStatusResp, SyncLogResp, ChecksumResp, PublicGroupInfoResp, SearchGroupsResp, DigestResp, ListMyGroupsResp, GetFileResp, GetSummaryResp, GetMetricsResp } from './types';
|
|
6
|
+
import { RegisterOnlineResp, CreateGroupResp, SendMessageResp, PullMessagesResp, PullEventsResp, CursorState, GroupInfoResp, BanlistResp, BatchReviewResp, PendingRequestsResp, MembersResp, AdminsResp, RulesResp, AnnouncementResp, JoinRequirementsResp, MasterResp, InviteCodeResp, InviteCodeListResp, BroadcastLockResp, BroadcastPermissionResp, SyncStatusResp, SyncLogResp, ChecksumResp, PublicGroupInfoResp, SearchGroupsResp, DigestResp, ListMyGroupsResp, GetFileResp, GetSummaryResp, GetMetricsResp } from './types';
|
|
7
7
|
/**
|
|
8
8
|
* Callback interface for syncGroup.
|
|
9
9
|
*/
|
|
@@ -42,15 +42,36 @@ export declare class GroupOperations {
|
|
|
42
42
|
inviteCode?: string;
|
|
43
43
|
message?: string;
|
|
44
44
|
}): Promise<string>;
|
|
45
|
+
/**
|
|
46
|
+
* 注册上线,获取游标状态。
|
|
47
|
+
* 客户端每次启动或重新连接时,必须对每个群组调用。
|
|
48
|
+
*/
|
|
49
|
+
registerOnline(targetAid: string, groupId: string): Promise<RegisterOnlineResp>;
|
|
50
|
+
/**
|
|
51
|
+
* 主动下线(优雅退出)。
|
|
52
|
+
* 客户端退出时调用,立即从在线列表移除。
|
|
53
|
+
*/
|
|
54
|
+
unregisterOnline(targetAid: string, groupId: string): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* 心跳保活。
|
|
57
|
+
* 在线注册有 5 分钟超时,SDK 需定时发送(建议 2~4 分钟)。
|
|
58
|
+
*/
|
|
59
|
+
heartbeat(targetAid: string, groupId: string): Promise<void>;
|
|
45
60
|
createGroup(targetAid: string, name: string, options?: {
|
|
46
61
|
alias?: string;
|
|
47
62
|
subject?: string;
|
|
48
63
|
visibility?: string;
|
|
64
|
+
description?: string;
|
|
49
65
|
tags?: string[];
|
|
50
66
|
}): Promise<CreateGroupResp>;
|
|
51
67
|
addMember(targetAid: string, groupId: string, agentId: string, role?: string): Promise<void>;
|
|
52
68
|
sendGroupMessage(targetAid: string, groupId: string, content: string, contentType?: string, metadata?: Record<string, any>): Promise<SendMessageResp>;
|
|
53
|
-
|
|
69
|
+
/**
|
|
70
|
+
* 拉取消息。
|
|
71
|
+
* - afterMsgId > 0: 指定位置模式,从该 ID 之后开始拉取
|
|
72
|
+
* - afterMsgId = 0 或不传: 自动游标模式(推荐),服务端基于 current_msg_id 自动计算
|
|
73
|
+
*/
|
|
74
|
+
pullMessages(targetAid: string, groupId: string, afterMsgId?: number, limit?: number): Promise<PullMessagesResp>;
|
|
54
75
|
ackMessages(targetAid: string, groupId: string, msgId: number): Promise<void>;
|
|
55
76
|
pullEvents(targetAid: string, groupId: string, afterEventId: number, limit?: number): Promise<PullEventsResp>;
|
|
56
77
|
ackEvents(targetAid: string, groupId: string, eventId: number): Promise<void>;
|
package/dist/group/operations.js
CHANGED
|
@@ -59,6 +59,37 @@ class GroupOperations {
|
|
|
59
59
|
return this.requestJoin(targetAid, groupId, (_a = options === null || options === void 0 ? void 0 : options.message) !== null && _a !== void 0 ? _a : '');
|
|
60
60
|
}
|
|
61
61
|
// ============================================================
|
|
62
|
+
// Phase 0: Lifecycle (register / heartbeat / unregister)
|
|
63
|
+
// ============================================================
|
|
64
|
+
/**
|
|
65
|
+
* 注册上线,获取游标状态。
|
|
66
|
+
* 客户端每次启动或重新连接时,必须对每个群组调用。
|
|
67
|
+
*/
|
|
68
|
+
async registerOnline(targetAid, groupId) {
|
|
69
|
+
const resp = await this._client.sendRequest(targetAid, groupId, "register_online", null);
|
|
70
|
+
this._check(resp, "register_online");
|
|
71
|
+
const d = resp.data || {};
|
|
72
|
+
return {
|
|
73
|
+
msg_cursor: (0, types_1.createMsgCursor)(d.msg_cursor),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* 主动下线(优雅退出)。
|
|
78
|
+
* 客户端退出时调用,立即从在线列表移除。
|
|
79
|
+
*/
|
|
80
|
+
async unregisterOnline(targetAid, groupId) {
|
|
81
|
+
const resp = await this._client.sendRequest(targetAid, groupId, "unregister_online", null);
|
|
82
|
+
this._check(resp, "unregister_online");
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 心跳保活。
|
|
86
|
+
* 在线注册有 5 分钟超时,SDK 需定时发送(建议 2~4 分钟)。
|
|
87
|
+
*/
|
|
88
|
+
async heartbeat(targetAid, groupId) {
|
|
89
|
+
const resp = await this._client.sendRequest(targetAid, groupId, "heartbeat", null);
|
|
90
|
+
this._check(resp, "heartbeat");
|
|
91
|
+
}
|
|
92
|
+
// ============================================================
|
|
62
93
|
// Phase 1: Basic Operations
|
|
63
94
|
// ============================================================
|
|
64
95
|
async createGroup(targetAid, name, options) {
|
|
@@ -70,6 +101,8 @@ class GroupOperations {
|
|
|
70
101
|
params.subject = options.subject;
|
|
71
102
|
if (options === null || options === void 0 ? void 0 : options.visibility)
|
|
72
103
|
params.visibility = options.visibility;
|
|
104
|
+
if (options === null || options === void 0 ? void 0 : options.description)
|
|
105
|
+
params.description = options.description;
|
|
73
106
|
if (options === null || options === void 0 ? void 0 : options.tags)
|
|
74
107
|
params.tags = options.tags;
|
|
75
108
|
const resp = await this._client.sendRequest(targetAid, "", "create_group", params);
|
|
@@ -96,12 +129,19 @@ class GroupOperations {
|
|
|
96
129
|
const d = resp.data || {};
|
|
97
130
|
return { msg_id: (_a = d.msg_id) !== null && _a !== void 0 ? _a : 0, timestamp: (_b = d.timestamp) !== null && _b !== void 0 ? _b : 0 };
|
|
98
131
|
}
|
|
99
|
-
|
|
132
|
+
/**
|
|
133
|
+
* 拉取消息。
|
|
134
|
+
* - afterMsgId > 0: 指定位置模式,从该 ID 之后开始拉取
|
|
135
|
+
* - afterMsgId = 0 或不传: 自动游标模式(推荐),服务端基于 current_msg_id 自动计算
|
|
136
|
+
*/
|
|
137
|
+
async pullMessages(targetAid, groupId, afterMsgId = 0, limit = 0) {
|
|
100
138
|
var _a, _b, _c;
|
|
101
|
-
const params = {
|
|
139
|
+
const params = {};
|
|
140
|
+
if (afterMsgId > 0)
|
|
141
|
+
params.after_msg_id = afterMsgId;
|
|
102
142
|
if (limit > 0)
|
|
103
143
|
params.limit = limit;
|
|
104
|
-
const resp = await this._client.sendRequest(targetAid, groupId, "pull_messages", params);
|
|
144
|
+
const resp = await this._client.sendRequest(targetAid, groupId, "pull_messages", Object.keys(params).length > 0 ? params : null);
|
|
105
145
|
this._check(resp, "pull_messages");
|
|
106
146
|
const d = resp.data || {};
|
|
107
147
|
return {
|
package/dist/group/types.d.ts
CHANGED