acp-ws
Version:
基于 WebSocket 的智能体通信库,提供智能体身份管理和实时通信功能
271 lines (270 loc) • 10.3 kB
JavaScript
import { v4 as uuidv4 } from 'uuid';
import mitt from 'mitt';
class WSClient {
constructor() {
this.emitter = mitt();
this.socket = null;
this.status = 'disconnected';
this.aid = '';
this.isRunning = false;
this.isOnline = false;
this.wsUrl = '';
this.maxRetries = 5;
this.isNormalClose = false;
}
async connectToServer(wsServer, aid, sinature) {
this.aid = aid;
let url = wsServer.replace("https://", "ws://").replace("http://", "ws://");
const encodedAid = encodeURIComponent(aid);
const encodedSignature = encodeURIComponent(sinature);
if (!this.isRunning) {
url = `${url}/session?agent_id=${encodedAid}&signature=${encodedSignature}`;
this.wsUrl = url;
await this.connect();
}
}
createSession(cb) {
this.createSessionId();
// 创建一个包装的回调函数,执行完后自动移除监听器
const wrappedCallback = (status) => {
cb(status);
this.emitter.off('session', wrappedCallback);
};
this.emitter.on('session', wrappedCallback);
}
invite(receiver, sessionId, identifyingCode, cb = null) {
this.sendInviteMessage(receiver, sessionId, identifyingCode);
if (cb) {
// 创建一个包装的回调函数,执行完后自动移除监听器
const wrappedCallback = (status) => {
cb(status);
this.emitter.off('invite', wrappedCallback);
};
this.emitter.on('invite', wrappedCallback);
}
}
onStatusChange(cb) {
this.emitter.on('status-change', cb);
}
onMessage(cb) {
this.emitter.on('message', cb);
}
disconnect() {
this.isRunning = false;
this.isNormalClose = true;
if (this.socket) {
this.socket.close();
this.socket = null;
}
this.emitter.all.clear();
}
send(message, to, sessionId, identifyingCode) {
var _a;
if (!message || message.trim().length === 0) {
console.error('发送的消息不能为空');
return;
}
if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
const errorMessage = [{
'content': 'WebSocket连接未建立或已断开'
}];
this.emitter.emit('message', {
type: 'error',
content: JSON.stringify(errorMessage)
});
return;
}
if (!this.isOnline) {
const errorMessage = [{
'content': `${to}不在线`
}];
this.emitter.emit('message', {
type: 'error',
content: JSON.stringify(errorMessage)
});
return;
}
try {
const message_data = [{
"type": "content",
"status": "success",
"timestamp": Date.now().toString(),
"content": message
}];
const jsonMsg = JSON.stringify(message_data);
const encodedMsg = encodeURIComponent(jsonMsg);
const message_id = Date.now().toString();
const msg = {
cmd: "session_message",
data: {
message_id: message_id,
session_id: sessionId,
ref_msg_id: '',
sender: this.aid,
receiver: to,
message: encodedMsg,
timestamp: Date.now().toString()
}
};
(_a = this.socket) === null || _a === void 0 ? void 0 : _a.send(JSON.stringify(msg));
}
catch (error) {
console.error('发送消息失败:', error);
this.emitter.emit('message', {
type: 'error',
content: JSON.stringify([{ content: '发送消息失败' }])
});
}
}
connect() {
return new Promise((resolve, reject) => {
let retryCount = 0;
const attemptConnect = () => {
this.updateStatus('connecting');
try {
this.socket = new WebSocket(this.wsUrl);
this.socket.onopen = () => {
this.isRunning = true;
this.updateStatus('connected');
resolve(); // 连接成功
};
this.socket.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
this.handleMessage(data);
}
catch (error) {
console.error('解析消息失败:', error);
}
};
this.socket.onclose = (event) => {
this.isRunning = false;
this.updateStatus('disconnected');
if (!this.isNormalClose && !event.wasClean && retryCount < this.maxRetries) {
retryCount++;
const retryDelay = retryCount * 1000;
console.warn(`WebSocket连接断开,${retryCount}秒后重试...`);
setTimeout(attemptConnect, retryDelay);
}
else if (!this.isNormalClose && retryCount >= this.maxRetries) {
reject(new Error(`WebSocket连接失败,已达到最大重试次数 (${this.maxRetries})`));
}
this.isNormalClose = false;
};
this.socket.onerror = (error) => {
var _a, _b;
console.error('WebSocket 错误:', {
error,
readyState: (_a = this.socket) === null || _a === void 0 ? void 0 : _a.readyState,
url: this.wsUrl,
});
this.updateStatus('error');
if (((_b = this.socket) === null || _b === void 0 ? void 0 : _b.readyState) === WebSocket.OPEN) {
this.socket.close(); // 主动触发 onclose
}
};
}
catch (err) {
console.error('创建 WebSocket 实例失败:', err);
this.updateStatus('error');
if (retryCount < this.maxRetries) {
retryCount++;
const retryDelay = retryCount * 1000;
console.warn(`创建失败,${retryCount}秒后重试...`);
setTimeout(attemptConnect, retryDelay);
}
else {
reject(new Error(`创建 WebSocket 实例失败,已达到最大重试次数 (${this.maxRetries})`));
}
}
};
attemptConnect();
});
}
/**
* 获取当前WebSocket连接状态
* @returns 当前连接状态
*/
getCurrentStatus() {
return this.status;
}
updateStatus(newStatus) {
if (this.status !== newStatus) {
this.status = newStatus;
this.emitter.emit('status-change', newStatus);
}
}
createSessionId() {
if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
console.error('WebSocket连接未建立,无法创建会话');
return;
}
try {
const msg = {
cmd: "create_session_req",
data: {
request_id: uuidv4().replace(/-/g, ''),
type: "public",
group_name: "1",
subject: "",
timestamp: Date.now().toString()
}
};
this.socket.send(JSON.stringify(msg));
}
catch (error) {
console.error('发送创建会话请求失败:', error);
}
}
sendInviteMessage(receiver, sessionId, identifyingCode) {
var _a;
if (!receiver || !sessionId || !identifyingCode) {
console.error('邀请参数不完整');
return;
}
if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
console.error('WebSocket连接未建立,无法发送邀请');
return;
}
try {
const msg = {
cmd: "invite_agent_req",
data: {
request_id: uuidv4().replace(/-/g, ''),
acceptor_id: receiver,
invite_code: identifyingCode,
session_id: sessionId,
inviter_id: this.aid
}
};
(_a = this.socket) === null || _a === void 0 ? void 0 : _a.send(JSON.stringify(msg));
}
catch (error) {
console.error('发送邀请失败:', error);
}
}
handleMessage(message) {
const { cmd, data } = message;
if (cmd === "create_session_ack") {
const { session_id, identifying_code } = data;
this.emitter.emit('session', {
sessionId: session_id !== null && session_id !== void 0 ? session_id : '',
identifyingCode: identifying_code !== null && identifying_code !== void 0 ? identifying_code : ''
});
}
else if (cmd === "invite_agent_ack") {
const { status_code } = data;
if (Number(status_code) === 200) {
this.isOnline = true;
}
else {
this.isOnline = false;
}
this.emitter.emit('invite', this.isOnline ? 'success' : 'error');
}
else if (cmd === "session_message") {
this.emitter.emit('message', data);
}
}
}
export { WSClient };