UNPKG

acp-ws

Version:

基于 WebSocket 的智能体通信库,提供智能体身份管理和实时通信功能

271 lines (270 loc) 10.3 kB
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 };