openclaw-stepfun 0.2.2
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/LICENSE +21 -0
- package/README.md +61 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +18 -0
- package/dist/src/accounts.d.ts +22 -0
- package/dist/src/accounts.js +43 -0
- package/dist/src/bot.d.ts +16 -0
- package/dist/src/bot.js +100 -0
- package/dist/src/channel.d.ts +4 -0
- package/dist/src/channel.js +206 -0
- package/dist/src/client.d.ts +51 -0
- package/dist/src/client.js +206 -0
- package/dist/src/monitor.d.ts +19 -0
- package/dist/src/monitor.js +153 -0
- package/dist/src/proto/capy/botauth/auth_common_pb.d.ts +82 -0
- package/dist/src/proto/capy/botauth/auth_common_pb.js +35 -0
- package/dist/src/proto/capy/botauth/botauth_connect.d.ts +118 -0
- package/dist/src/proto/capy/botauth/botauth_connect.js +118 -0
- package/dist/src/proto/capy/botauth/botauth_pb.d.ts +1065 -0
- package/dist/src/proto/capy/botauth/botauth_pb.js +348 -0
- package/dist/src/proto/capy/botauth/public_connect.d.ts +62 -0
- package/dist/src/proto/capy/botauth/public_connect.js +62 -0
- package/dist/src/proto/capy/botauth/public_pb.d.ts +254 -0
- package/dist/src/proto/capy/botauth/public_pb.js +105 -0
- package/dist/src/proto/capy/botmsg/botmsg_connect.d.ts +72 -0
- package/dist/src/proto/capy/botmsg/botmsg_connect.js +72 -0
- package/dist/src/proto/capy/botmsg/botmsg_pb.d.ts +426 -0
- package/dist/src/proto/capy/botmsg/botmsg_pb.js +160 -0
- package/dist/src/proto/capy/botway/ctrl_connect.d.ts +61 -0
- package/dist/src/proto/capy/botway/ctrl_connect.js +61 -0
- package/dist/src/proto/capy/botway/ctrl_pb.d.ts +267 -0
- package/dist/src/proto/capy/botway/ctrl_pb.js +120 -0
- package/dist/src/proto/capy/botway/stream_connect.d.ts +26 -0
- package/dist/src/proto/capy/botway/stream_connect.js +26 -0
- package/dist/src/proto/capy/botway/stream_pb.d.ts +495 -0
- package/dist/src/proto/capy/botway/stream_pb.js +165 -0
- package/dist/src/reply-dispatcher.d.ts +17 -0
- package/dist/src/reply-dispatcher.js +234 -0
- package/dist/src/runtime.d.ts +4 -0
- package/dist/src/runtime.js +11 -0
- package/dist/src/send.d.ts +19 -0
- package/dist/src/send.js +66 -0
- package/dist/src/types.d.ts +65 -0
- package/dist/src/types.js +2 -0
- package/dist/src/websocket/cacheEvent.d.ts +17 -0
- package/dist/src/websocket/cacheEvent.js +61 -0
- package/dist/src/websocket/connect.d.ts +32 -0
- package/dist/src/websocket/connect.js +79 -0
- package/dist/src/websocket/constant.d.ts +8 -0
- package/dist/src/websocket/constant.js +10 -0
- package/dist/src/websocket/eventBus.d.ts +15 -0
- package/dist/src/websocket/eventBus.js +46 -0
- package/dist/src/websocket/index.d.ts +117 -0
- package/dist/src/websocket/index.js +637 -0
- package/dist/src/websocket/service.d.ts +36 -0
- package/dist/src/websocket/service.js +4 -0
- package/dist/src/websocket/stream.d.ts +10 -0
- package/dist/src/websocket/stream.js +24 -0
- package/dist/src/websocket/streamConnect.d.ts +40 -0
- package/dist/src/websocket/streamConnect.js +48 -0
- package/openclaw.plugin.json +23 -0
- package/package.json +69 -0
- package/scripts/setup.mjs +381 -0
- package/scripts/switch-env.mjs +98 -0
|
@@ -0,0 +1,637 @@
|
|
|
1
|
+
import { ErrorRes, HeartbeatReq, StreamMsg, } from "./streamConnect.js";
|
|
2
|
+
import { BotAuthBindReq, BotAuthBindRes, } from "../proto/capy/botway/stream_pb.js";
|
|
3
|
+
import { CacheEvent } from "./cacheEvent.js";
|
|
4
|
+
// import { useAuthStore } from '@/store/authInfo';
|
|
5
|
+
import { commonEventBus, Event } from "./eventBus.js";
|
|
6
|
+
// import { refreshToken } from "..";
|
|
7
|
+
import { BASE_URL, Env } from "./constant.js";
|
|
8
|
+
import { MethodKind } from "@bufbuild/protobuf";
|
|
9
|
+
const cache = new CacheEvent();
|
|
10
|
+
export const uuid = () => {
|
|
11
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
12
|
+
const r = (Math.random() * 16) | 0;
|
|
13
|
+
const v = c === "x" ? r : (r & 0x3) | 0x8;
|
|
14
|
+
return v.toString(16);
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
export var SocketConnectState;
|
|
18
|
+
(function (SocketConnectState) {
|
|
19
|
+
SocketConnectState["Initial"] = "initial";
|
|
20
|
+
SocketConnectState["Connecting"] = "connecting";
|
|
21
|
+
SocketConnectState["Connected"] = "connected";
|
|
22
|
+
SocketConnectState["Token"] = "token";
|
|
23
|
+
SocketConnectState["Auth"] = "auth";
|
|
24
|
+
SocketConnectState["Disconnect"] = "disconnect";
|
|
25
|
+
})(SocketConnectState || (SocketConnectState = {}));
|
|
26
|
+
export var AuthState;
|
|
27
|
+
(function (AuthState) {
|
|
28
|
+
// 鉴权状态
|
|
29
|
+
AuthState["Initial"] = "initial";
|
|
30
|
+
AuthState["Anonymous"] = "anonymous";
|
|
31
|
+
AuthState["User"] = "user";
|
|
32
|
+
})(AuthState || (AuthState = {}));
|
|
33
|
+
export var ErrorCode;
|
|
34
|
+
(function (ErrorCode) {
|
|
35
|
+
ErrorCode[ErrorCode["Common"] = 1] = "Common";
|
|
36
|
+
ErrorCode[ErrorCode["Auth"] = 2] = "Auth";
|
|
37
|
+
ErrorCode[ErrorCode["Socket"] = 3] = "Socket";
|
|
38
|
+
ErrorCode[ErrorCode["Login"] = -1006] = "Login";
|
|
39
|
+
})(ErrorCode || (ErrorCode = {}));
|
|
40
|
+
// 鉴权包错误
|
|
41
|
+
export var AuthSocketError;
|
|
42
|
+
(function (AuthSocketError) {
|
|
43
|
+
// 鉴权失败,未知原因
|
|
44
|
+
AuthSocketError[AuthSocketError["AuthCodeFail"] = -1001] = "AuthCodeFail";
|
|
45
|
+
// 鉴权失败,access token过期
|
|
46
|
+
AuthSocketError[AuthSocketError["AuthCodeExpired"] = -1002] = "AuthCodeExpired";
|
|
47
|
+
// 鉴权失败,用户被封禁
|
|
48
|
+
AuthSocketError[AuthSocketError["AuthCodeBanned"] = -1003] = "AuthCodeBanned";
|
|
49
|
+
// 鉴权失败,用户未被激活
|
|
50
|
+
AuthSocketError[AuthSocketError["AuthCodeNotActivated"] = -1004] = "AuthCodeNotActivated";
|
|
51
|
+
})(AuthSocketError || (AuthSocketError = {}));
|
|
52
|
+
export var WebsocketCloseCode;
|
|
53
|
+
(function (WebsocketCloseCode) {
|
|
54
|
+
// CloseCodeNormal 正常关闭
|
|
55
|
+
WebsocketCloseCode[WebsocketCloseCode["CloseCodeNormal"] = 1000] = "CloseCodeNormal";
|
|
56
|
+
// 客户端主动断连
|
|
57
|
+
WebsocketCloseCode[WebsocketCloseCode["CloseCodeErrorClient"] = 1001] = "CloseCodeErrorClient";
|
|
58
|
+
// 客户端主动断连
|
|
59
|
+
WebsocketCloseCode[WebsocketCloseCode["CloseCodeClient"] = 1006] = "CloseCodeClient";
|
|
60
|
+
// CloseCodeAuthFailed 鉴权失败
|
|
61
|
+
WebsocketCloseCode[WebsocketCloseCode["CloseCodeAuthFailed"] = 4001] = "CloseCodeAuthFailed";
|
|
62
|
+
//CloseCodeAuthTimeout 鉴权超时失败
|
|
63
|
+
WebsocketCloseCode[WebsocketCloseCode["CloseCodeAuthTimeout"] = 4002] = "CloseCodeAuthTimeout";
|
|
64
|
+
//CloseCodeHeartbeatTimeout 心跳超时失败
|
|
65
|
+
WebsocketCloseCode[WebsocketCloseCode["CloseCodeHeartbeatTimeout"] = 4003] = "CloseCodeHeartbeatTimeout";
|
|
66
|
+
// CloseCodeSameUserReconnect 同用户连接冲突
|
|
67
|
+
WebsocketCloseCode[WebsocketCloseCode["CloseCodeSameUserReconnect"] = 4004] = "CloseCodeSameUserReconnect";
|
|
68
|
+
// CloseCodeKickOut 踢下线
|
|
69
|
+
WebsocketCloseCode[WebsocketCloseCode["CloseCodeKickOut"] = 4005] = "CloseCodeKickOut";
|
|
70
|
+
// CloseCodeUserForbidden 用户封禁
|
|
71
|
+
WebsocketCloseCode[WebsocketCloseCode["CloseCodeUserForbidden"] = 4006] = "CloseCodeUserForbidden";
|
|
72
|
+
// CloseCodeParseMsgFailed 解析数据失败
|
|
73
|
+
WebsocketCloseCode[WebsocketCloseCode["CloseCodeParseMsgFailed"] = 4007] = "CloseCodeParseMsgFailed";
|
|
74
|
+
// CloseCodeServerRestart 服务重启
|
|
75
|
+
WebsocketCloseCode[WebsocketCloseCode["CloseCodeServerRestart"] = 4008] = "CloseCodeServerRestart";
|
|
76
|
+
})(WebsocketCloseCode || (WebsocketCloseCode = {}));
|
|
77
|
+
const RECONNECT_CODES = [
|
|
78
|
+
WebsocketCloseCode.CloseCodeNormal,
|
|
79
|
+
WebsocketCloseCode.CloseCodeErrorClient,
|
|
80
|
+
WebsocketCloseCode.CloseCodeClient,
|
|
81
|
+
// WebsocketCloseCode.CloseCodeAuthTimeout,
|
|
82
|
+
WebsocketCloseCode.CloseCodeHeartbeatTimeout,
|
|
83
|
+
WebsocketCloseCode.CloseCodeParseMsgFailed,
|
|
84
|
+
WebsocketCloseCode.CloseCodeServerRestart,
|
|
85
|
+
];
|
|
86
|
+
const RETRY_MAX = 3;
|
|
87
|
+
const CONNECT_RETRY_MAX = 7;
|
|
88
|
+
const socketEvent = new Event();
|
|
89
|
+
class _Socket {
|
|
90
|
+
_conn = null;
|
|
91
|
+
// static isConnected = false
|
|
92
|
+
// static _connecting = 0
|
|
93
|
+
_state = SocketConnectState.Initial; // 当前的连接状态
|
|
94
|
+
_authState = AuthState.Initial; // 当前的鉴权状态
|
|
95
|
+
_sendQueue = [];
|
|
96
|
+
_connectId = null;
|
|
97
|
+
_retryAuthTime = 0; // 鉴权重试次数
|
|
98
|
+
_retryConnectTime = 0; // 重连重试次数
|
|
99
|
+
_heartHandler = null; // todo
|
|
100
|
+
_requestTasks = new Set();
|
|
101
|
+
_requestStreams = new Set();
|
|
102
|
+
auth = null;
|
|
103
|
+
heartbeatTime = 30 * 1000;
|
|
104
|
+
errorCount = 0;
|
|
105
|
+
testCount = 0;
|
|
106
|
+
limit = 5;
|
|
107
|
+
events = socketEvent;
|
|
108
|
+
onAuthErr;
|
|
109
|
+
onAuthSuccess;
|
|
110
|
+
constructor(options) {
|
|
111
|
+
this.heartbeatTime = options?.heartbeatTime || this.heartbeatTime;
|
|
112
|
+
this.onAuthErr = options?.onAuthErr;
|
|
113
|
+
this.onAuthSuccess = options?.onAuthSuccess;
|
|
114
|
+
if (!options?.ignoreCreate) {
|
|
115
|
+
// Use void to explicitly mark this as intentionally unawaited
|
|
116
|
+
// Errors are handled internally by the socket's event system
|
|
117
|
+
void this.create("new socket").catch((err) => {
|
|
118
|
+
console.error("[websocket] Constructor create failed:", err);
|
|
119
|
+
// Error is logged but not thrown, to prevent affecting the gateway
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
commonEventBus.on("tokenUpdate", () => {
|
|
123
|
+
// Wrap sendAuth to catch any errors and prevent unhandled rejections
|
|
124
|
+
this.sendAuth().catch((err) => {
|
|
125
|
+
console.error("[websocket] sendAuth failed:", err);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
// Custom WebSocket URL
|
|
130
|
+
_customWsUrl;
|
|
131
|
+
async create(scene, url) {
|
|
132
|
+
if (this._state !== SocketConnectState.Initial && this._conn) {
|
|
133
|
+
console.log(`[websocket] create skipped - already has connection (state: ${this._state})`);
|
|
134
|
+
return this._conn;
|
|
135
|
+
}
|
|
136
|
+
const wsUrl = url || this._customWsUrl || BASE_URL[Env.Prod].ws;
|
|
137
|
+
console.log(`[websocket] creating new connection - scene: "${scene}", url: ${wsUrl.replace(/token=[^&]+/, "token=***")}`);
|
|
138
|
+
this._state = SocketConnectState.Connecting; // 将状态变更为连接中
|
|
139
|
+
const conn = new WebSocket(wsUrl);
|
|
140
|
+
conn.binaryType = "arraybuffer";
|
|
141
|
+
this._conn = conn;
|
|
142
|
+
cache.clear();
|
|
143
|
+
// Add event handlers BEFORE awaiting initAuth to avoid race condition
|
|
144
|
+
// where WebSocket connects before handlers are attached
|
|
145
|
+
this.addConnEvents();
|
|
146
|
+
try {
|
|
147
|
+
await this.initAuth();
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
console.error("[websocket] initAuth failed in create:", err);
|
|
151
|
+
// Don't throw - let the connection attempt continue
|
|
152
|
+
// The auth will be retried or the connection will fail naturally
|
|
153
|
+
}
|
|
154
|
+
this._state = SocketConnectState.Connecting; // 将状态变更为连接中
|
|
155
|
+
return conn;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Set custom WebSocket URL
|
|
159
|
+
*/
|
|
160
|
+
setUrl(url) {
|
|
161
|
+
this._customWsUrl = url;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Set authentication credentials
|
|
165
|
+
*/
|
|
166
|
+
setAuth(token, appId) {
|
|
167
|
+
this.auth = new BotAuthBindReq({
|
|
168
|
+
token,
|
|
169
|
+
appId,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
async initAuth() {
|
|
173
|
+
// 获取鉴权信息
|
|
174
|
+
try {
|
|
175
|
+
// Auth should be set externally via setAuth() before connection
|
|
176
|
+
// If not set, we'll wait for it to be set later
|
|
177
|
+
if (!this.auth) {
|
|
178
|
+
console.log("[websocket]initAuth: auth not set, waiting for setAuth() call");
|
|
179
|
+
// Don't throw error, just don't complete the cache
|
|
180
|
+
// The connection will wait for auth to be set
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
console.log("[websocket]initAuth", this.auth.token);
|
|
184
|
+
cache.complete("getToken");
|
|
185
|
+
}
|
|
186
|
+
catch (e) {
|
|
187
|
+
console.error("[websocket]initAuth error:", e);
|
|
188
|
+
// Use setTimeout to avoid stack overflow from recursive calls
|
|
189
|
+
if (this._retryAuthTime < RETRY_MAX) {
|
|
190
|
+
this._retryAuthTime += 1;
|
|
191
|
+
setTimeout(() => {
|
|
192
|
+
void this.initAuth();
|
|
193
|
+
}, 1000 * this._retryAuthTime); // Exponential backoff
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
console.error("[websocket]initAuth: max retries exceeded");
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
addConnEvents() {
|
|
201
|
+
if (!this._conn)
|
|
202
|
+
return;
|
|
203
|
+
const { _conn } = this;
|
|
204
|
+
if (_conn.onmessage)
|
|
205
|
+
return;
|
|
206
|
+
_conn.onopen = () => {
|
|
207
|
+
console.log(`[websocket] WebSocket connected - transitioning to auth phase`);
|
|
208
|
+
this._state = SocketConnectState.Connected;
|
|
209
|
+
cache.do(async () => {
|
|
210
|
+
this._state = SocketConnectState.Token; // 保证状态变化的线性,所以在connected后再变更状态至token
|
|
211
|
+
try {
|
|
212
|
+
await this.sendAuth();
|
|
213
|
+
console.log(`[websocket] connection fully established - auth successful, state: ${this._state}`);
|
|
214
|
+
this.events.emit("open");
|
|
215
|
+
this.errorCount = 0;
|
|
216
|
+
}
|
|
217
|
+
catch (err) {
|
|
218
|
+
// Auth failed - this is expected when credentials are invalid
|
|
219
|
+
// Error is already logged by sendAuth, just ensure we don't crash
|
|
220
|
+
console.error("[websocket] Auth failed in onopen:", err);
|
|
221
|
+
// Don't rethrow - let the connection close and retry handle it
|
|
222
|
+
}
|
|
223
|
+
}, ["getToken"]);
|
|
224
|
+
};
|
|
225
|
+
_conn.onmessage = (event) => {
|
|
226
|
+
// 假如是心跳包,只发不解
|
|
227
|
+
if (event.data instanceof ArrayBuffer) {
|
|
228
|
+
const uint8Array = new Uint8Array(event.data);
|
|
229
|
+
const { body, head } = StreamMsg.fromBinary(uint8Array);
|
|
230
|
+
this.events.emit("message", body);
|
|
231
|
+
// this.testCount++;
|
|
232
|
+
// if (this.testCount >= this.limit) {
|
|
233
|
+
// console.log('==lin==manual onclose');
|
|
234
|
+
// _conn.onclose?.(
|
|
235
|
+
// new CloseEvent('', {
|
|
236
|
+
// code: WebsocketCloseCode.CloseCodeClient,
|
|
237
|
+
// }),
|
|
238
|
+
// );
|
|
239
|
+
// this.testCount = 0;
|
|
240
|
+
// this.limit = this.limit * 2;
|
|
241
|
+
// }
|
|
242
|
+
if (head?.msgType === 4 || !body) {
|
|
243
|
+
// todo 要定义枚举值
|
|
244
|
+
const error = body
|
|
245
|
+
? ErrorRes.fromBinary(body)
|
|
246
|
+
: new ErrorRes({ code: 1, reason: "回包异常" });
|
|
247
|
+
this.events.emit(`error:${head?.msgId}`, error);
|
|
248
|
+
this.events.emit(`error:${head?.module}/${head?.command}`, error);
|
|
249
|
+
this.events.emit(`error`, error);
|
|
250
|
+
if (error.code === ErrorCode.Login) {
|
|
251
|
+
this.onError(new ErrorRes({ code: ErrorCode.Login, reason: "login required" }));
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
this.onError(error);
|
|
255
|
+
}
|
|
256
|
+
// this.testCount = 0;
|
|
257
|
+
// this.limit = this.limit * 5;
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
if (head?.msgId) {
|
|
261
|
+
this.events.emit(head?.msgId, body);
|
|
262
|
+
this._requestTasks.delete(head?.msgId);
|
|
263
|
+
}
|
|
264
|
+
if (head?.command && head?.module) {
|
|
265
|
+
this.events.emit(`${head.module}/${head.command}`, body);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
_conn.onerror = (e) => {
|
|
270
|
+
console.error("[websocket] WebSocket error occurred:", e);
|
|
271
|
+
};
|
|
272
|
+
_conn.onclose = (e) => {
|
|
273
|
+
// 给没完成的请求发错误通知
|
|
274
|
+
this._requestTasks.forEach((v) => {
|
|
275
|
+
this.events.emit(`error:${v}`, new ErrorRes({
|
|
276
|
+
code: 1,
|
|
277
|
+
reason: "network error",
|
|
278
|
+
}));
|
|
279
|
+
});
|
|
280
|
+
this._requestTasks = new Set();
|
|
281
|
+
this._requestStreams.forEach((v) => {
|
|
282
|
+
this.events.emit(`error:${v}`, new ErrorRes({
|
|
283
|
+
code: 1,
|
|
284
|
+
reason: "network error",
|
|
285
|
+
}));
|
|
286
|
+
});
|
|
287
|
+
this._requestStreams = new Set();
|
|
288
|
+
// 正常关闭
|
|
289
|
+
if (this._retryConnectTime > CONNECT_RETRY_MAX) {
|
|
290
|
+
// 重试10次失败忽视
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
// 打印详细的关闭原因
|
|
294
|
+
const closeReason = this.getCloseReason(e.code);
|
|
295
|
+
const wasClean = e.wasClean ? "clean" : "unclean";
|
|
296
|
+
console.log(`[websocket] connection closed - code: ${e.code} (${closeReason}), reason: "${e.reason || "N/A"}", wasClean: ${wasClean}, retryCount: ${this._retryConnectTime}`);
|
|
297
|
+
this._retryConnectTime += 1;
|
|
298
|
+
this.events.emit("disconnect", e);
|
|
299
|
+
if (RECONNECT_CODES.includes(e.code)) {
|
|
300
|
+
const willReconnect = RECONNECT_CODES.includes(e.code);
|
|
301
|
+
console.log(`[websocket] close code ${e.code} (${closeReason}) is in RECONNECT_CODES, will reconnect: ${willReconnect}`);
|
|
302
|
+
this.events.emit("close", e);
|
|
303
|
+
this.reconnect(`onclose_${closeReason}`);
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
console.log(`[websocket] close code ${e.code} (${closeReason}) is NOT in RECONNECT_CODES, no automatic reconnect will be triggered`);
|
|
307
|
+
}
|
|
308
|
+
// 鉴权失败
|
|
309
|
+
if (e.code === WebsocketCloseCode.CloseCodeAuthFailed ||
|
|
310
|
+
e.code === WebsocketCloseCode.CloseCodeAuthTimeout ||
|
|
311
|
+
e.code === WebsocketCloseCode.CloseCodeKickOut) {
|
|
312
|
+
console.log(`[websocket] auth error detected (code: ${e.code}), clearing auth and reconnecting...`);
|
|
313
|
+
this.clearAuth();
|
|
314
|
+
this.reconnect("auth_error");
|
|
315
|
+
}
|
|
316
|
+
if (e.code === WebsocketCloseCode.CloseCodeKickOut ||
|
|
317
|
+
e.code === WebsocketCloseCode.CloseCodeUserForbidden) {
|
|
318
|
+
console.log(`[websocket] user kicked out or forbidden (code: ${e.code}), clearing auth and reconnecting...`);
|
|
319
|
+
this.clearAuth();
|
|
320
|
+
// useAuthStore.getState().logout();
|
|
321
|
+
this.reconnect("logout");
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
async onError(err) {
|
|
326
|
+
try {
|
|
327
|
+
if (err.code === ErrorCode.Auth && this.onAuthErr) {
|
|
328
|
+
this._state = SocketConnectState.Initial;
|
|
329
|
+
// 鉴权失败回调
|
|
330
|
+
this._authState = AuthState.Initial;
|
|
331
|
+
this.auth = null;
|
|
332
|
+
// this.disconnect('error_auth');
|
|
333
|
+
this.onAuthErr();
|
|
334
|
+
}
|
|
335
|
+
else if (this.errorCount < 5) {
|
|
336
|
+
if (err.code === -1002) {
|
|
337
|
+
// await refreshToken();
|
|
338
|
+
}
|
|
339
|
+
this._state = SocketConnectState.Initial;
|
|
340
|
+
void this.reconnect("onerror");
|
|
341
|
+
this.errorCount++;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
catch (e) {
|
|
345
|
+
console.error("[websocket] onError handler failed:", e);
|
|
346
|
+
// Error is logged but not thrown, to prevent affecting the gateway
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
clearAuth() {
|
|
350
|
+
this._authState = AuthState.Initial;
|
|
351
|
+
this.auth = null;
|
|
352
|
+
}
|
|
353
|
+
startHeartbeat() {
|
|
354
|
+
if (this._heartHandler) {
|
|
355
|
+
clearTimeout(this._heartHandler);
|
|
356
|
+
this._heartHandler = null;
|
|
357
|
+
}
|
|
358
|
+
this.heartbeat();
|
|
359
|
+
this._heartHandler = setTimeout(() => {
|
|
360
|
+
this.startHeartbeat();
|
|
361
|
+
}, this.heartbeatTime);
|
|
362
|
+
}
|
|
363
|
+
// 发鉴权包
|
|
364
|
+
sendAuth(oMsgId) {
|
|
365
|
+
const msgId = oMsgId || uuid();
|
|
366
|
+
console.log("[websocket]sendAuth", this.auth, msgId);
|
|
367
|
+
return new Promise((resolve, reject) => {
|
|
368
|
+
if (!this.auth) {
|
|
369
|
+
this.onError(new ErrorRes({ code: ErrorCode.Auth, reason: "没有鉴权信息" }));
|
|
370
|
+
return msgId;
|
|
371
|
+
}
|
|
372
|
+
this.send({
|
|
373
|
+
module: "Stream",
|
|
374
|
+
command: "Auth",
|
|
375
|
+
body: this.auth.toBinary(),
|
|
376
|
+
}, msgId, true);
|
|
377
|
+
this.on(msgId, (buff) => {
|
|
378
|
+
if (buff instanceof ErrorRes) {
|
|
379
|
+
const err = new ErrorRes({
|
|
380
|
+
code: ErrorCode.Auth,
|
|
381
|
+
reason: "auth error",
|
|
382
|
+
});
|
|
383
|
+
console.log("[websocket]sendAuth error", buff);
|
|
384
|
+
this.handleAuthError(err?.code);
|
|
385
|
+
this.onError(err);
|
|
386
|
+
reject(err);
|
|
387
|
+
return msgId;
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
const data = BotAuthBindRes.fromBinary(buff);
|
|
391
|
+
console.log("[websocket]sendAuth data", data);
|
|
392
|
+
const uid = data?.uid.toString() || "";
|
|
393
|
+
// if (data.logined) {
|
|
394
|
+
// useAuthStore.getState().login(uid);
|
|
395
|
+
// } else {
|
|
396
|
+
// useAuthStore.getState().logout();
|
|
397
|
+
// }
|
|
398
|
+
this._connectId = data.conn || null;
|
|
399
|
+
if (data?.code) {
|
|
400
|
+
const err = new ErrorRes({
|
|
401
|
+
code: ErrorCode.Auth,
|
|
402
|
+
reason: `auth error${JSON.stringify(data)}`,
|
|
403
|
+
});
|
|
404
|
+
this.handleAuthError(data?.code);
|
|
405
|
+
this.onError(err);
|
|
406
|
+
reject(err);
|
|
407
|
+
return msgId;
|
|
408
|
+
}
|
|
409
|
+
const logined = data.uid;
|
|
410
|
+
this._state = SocketConnectState.Auth;
|
|
411
|
+
this._authState = logined ? AuthState.User : AuthState.Anonymous;
|
|
412
|
+
this._retryConnectTime = 0; // 连接成功后重置重连次数
|
|
413
|
+
this.startHeartbeat(); // 鉴权成功才开始发心跳包
|
|
414
|
+
this._sendQueue.forEach(({ msgId, data }) => {
|
|
415
|
+
this.send(data, msgId);
|
|
416
|
+
});
|
|
417
|
+
this._sendQueue = [];
|
|
418
|
+
this.events.emit("connected", { logined, uid: data.uid.toString() });
|
|
419
|
+
if (this.onAuthSuccess) {
|
|
420
|
+
this.onAuthSuccess();
|
|
421
|
+
}
|
|
422
|
+
resolve(this._authState);
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
// 发心跳包
|
|
428
|
+
heartbeat(oMsgId) {
|
|
429
|
+
const msgId = oMsgId || uuid();
|
|
430
|
+
this.send({
|
|
431
|
+
module: "Stream",
|
|
432
|
+
command: "Heartbeat",
|
|
433
|
+
body: new HeartbeatReq({}).toBinary(),
|
|
434
|
+
}, msgId);
|
|
435
|
+
let isReceivedHeartbeat = 0;
|
|
436
|
+
this.on(msgId, (buff) => {
|
|
437
|
+
isReceivedHeartbeat = 1;
|
|
438
|
+
});
|
|
439
|
+
return msgId;
|
|
440
|
+
}
|
|
441
|
+
// 发普通包
|
|
442
|
+
send(params, oMsgId, isAuthPack) {
|
|
443
|
+
const msgId = oMsgId || uuid();
|
|
444
|
+
const { body: _, type = MethodKind.Unary, ...rest } = params;
|
|
445
|
+
if (params.command !== "Heartbeat") {
|
|
446
|
+
if (type === MethodKind.Unary) {
|
|
447
|
+
this._requestTasks.add(msgId);
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
this._requestStreams.add(msgId);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
if (this._retryConnectTime >= CONNECT_RETRY_MAX) {
|
|
454
|
+
this.disconnect("send_retry");
|
|
455
|
+
}
|
|
456
|
+
this._retryConnectTime = 0; // 重置重连次数
|
|
457
|
+
if (!this._conn ||
|
|
458
|
+
this._state === SocketConnectState.Initial ||
|
|
459
|
+
this._conn?.readyState !== WebSocket.OPEN) {
|
|
460
|
+
if (!isAuthPack) {
|
|
461
|
+
// 非鉴权包才入请求队列
|
|
462
|
+
this._sendQueue.push({
|
|
463
|
+
msgId,
|
|
464
|
+
data: params,
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
this.create("send");
|
|
468
|
+
return msgId;
|
|
469
|
+
}
|
|
470
|
+
if (!isAuthPack && this._state !== SocketConnectState.Auth) {
|
|
471
|
+
this._sendQueue.push({
|
|
472
|
+
msgId,
|
|
473
|
+
data: params,
|
|
474
|
+
});
|
|
475
|
+
return msgId;
|
|
476
|
+
}
|
|
477
|
+
const { module, command, body } = params;
|
|
478
|
+
if (!body) {
|
|
479
|
+
return msgId;
|
|
480
|
+
}
|
|
481
|
+
const data = {
|
|
482
|
+
head: {
|
|
483
|
+
frameType: 1, // 包体数据类型,0=text, 1=pb
|
|
484
|
+
msgType: 0, // 0=上行req,1=上行req的回包rsp,2=下行的push,3=下行push的回包(ack)
|
|
485
|
+
msgId: msgId,
|
|
486
|
+
module,
|
|
487
|
+
command,
|
|
488
|
+
reserved: {},
|
|
489
|
+
},
|
|
490
|
+
body,
|
|
491
|
+
};
|
|
492
|
+
console.log("[websocket]send ", new Date().toLocaleTimeString(), JSON.stringify({
|
|
493
|
+
command,
|
|
494
|
+
module,
|
|
495
|
+
_connectId: this._connectId,
|
|
496
|
+
msgId,
|
|
497
|
+
}));
|
|
498
|
+
try {
|
|
499
|
+
this._conn.send(new StreamMsg(data).toBinary());
|
|
500
|
+
}
|
|
501
|
+
catch (e) {
|
|
502
|
+
if (!isAuthPack) {
|
|
503
|
+
// 非鉴权包才加入请求队列
|
|
504
|
+
this._sendQueue.push({
|
|
505
|
+
msgId,
|
|
506
|
+
data: params,
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
this.create("send catch");
|
|
510
|
+
}
|
|
511
|
+
return msgId;
|
|
512
|
+
}
|
|
513
|
+
// 主动断连
|
|
514
|
+
disconnect(type, onClose) {
|
|
515
|
+
const readyState = this._conn?.readyState;
|
|
516
|
+
const stateNames = {
|
|
517
|
+
[WebSocket.CONNECTING]: "CONNECTING",
|
|
518
|
+
[WebSocket.OPEN]: "OPEN",
|
|
519
|
+
[WebSocket.CLOSING]: "CLOSING",
|
|
520
|
+
[WebSocket.CLOSED]: "CLOSED",
|
|
521
|
+
};
|
|
522
|
+
console.log(`[websocket] disconnect initiated - reason: "${type}", currentState: ${this._state}, readyState: ${stateNames[readyState ?? -1] || readyState}`);
|
|
523
|
+
if (!this._conn || this._state === SocketConnectState.Initial) {
|
|
524
|
+
console.log(`[websocket] disconnect skipped - no active connection`);
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
if (this._conn?.readyState !== WebSocket.CLOSED) {
|
|
528
|
+
try {
|
|
529
|
+
this._conn.onclose = (e) => {
|
|
530
|
+
console.log(`[websocket] connection closed by disconnect() - code: ${e.code}, reason: "${e.reason || "N/A"}"`);
|
|
531
|
+
this._conn = null;
|
|
532
|
+
setTimeout(() => {
|
|
533
|
+
onClose?.();
|
|
534
|
+
}, 1000);
|
|
535
|
+
};
|
|
536
|
+
this._conn.close();
|
|
537
|
+
}
|
|
538
|
+
catch (e) {
|
|
539
|
+
console.error("[websocket] disconnect error during close():", e);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
else {
|
|
543
|
+
console.log(`[websocket] disconnect skipped - connection already closed (readyState: ${readyState})`);
|
|
544
|
+
}
|
|
545
|
+
this.clearConnection();
|
|
546
|
+
}
|
|
547
|
+
// 重连
|
|
548
|
+
async reconnect(scene) {
|
|
549
|
+
console.log(`[websocket] starting reconnect sequence - trigger: "${scene}", attempt: ${this._retryConnectTime + 1}/${CONNECT_RETRY_MAX}`);
|
|
550
|
+
this.disconnect("connect", async () => {
|
|
551
|
+
try {
|
|
552
|
+
console.log(`[websocket] creating new connection after disconnect...`);
|
|
553
|
+
await this.create("reconnect");
|
|
554
|
+
console.log(`[websocket] reconnect successful`);
|
|
555
|
+
this.events.emit("reconnect");
|
|
556
|
+
}
|
|
557
|
+
catch (err) {
|
|
558
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
559
|
+
console.error(`[websocket] reconnect failed: ${errorMsg}`);
|
|
560
|
+
// Error is logged but not thrown, to prevent affecting the gateway
|
|
561
|
+
}
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
// 清除连接
|
|
565
|
+
clearConnection() {
|
|
566
|
+
this._conn = null;
|
|
567
|
+
this._connectId = null;
|
|
568
|
+
this.events.off("close");
|
|
569
|
+
this.events.off("open");
|
|
570
|
+
this.events.off("disconnect");
|
|
571
|
+
this.events.off("message");
|
|
572
|
+
// this.events.off('auth-error');
|
|
573
|
+
// this.events = new Event();
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* 获取 WebSocket 关闭代码的文字说明
|
|
577
|
+
*/
|
|
578
|
+
getCloseReason(code) {
|
|
579
|
+
const reasons = {
|
|
580
|
+
1000: "normal closure",
|
|
581
|
+
1001: "going away",
|
|
582
|
+
1002: "protocol error",
|
|
583
|
+
1003: "unsupported data",
|
|
584
|
+
1005: "no status received",
|
|
585
|
+
1006: "abnormal closure",
|
|
586
|
+
1007: "invalid frame payload data",
|
|
587
|
+
1008: "policy violation",
|
|
588
|
+
1009: "message too big",
|
|
589
|
+
1010: "mandatory extension",
|
|
590
|
+
1011: "internal server error",
|
|
591
|
+
1012: "service restart",
|
|
592
|
+
1013: "try again later",
|
|
593
|
+
1014: "bad gateway",
|
|
594
|
+
1015: "TLS handshake",
|
|
595
|
+
4001: "auth failed",
|
|
596
|
+
4002: "auth timeout",
|
|
597
|
+
4003: "heartbeat timeout",
|
|
598
|
+
4004: "same user reconnect",
|
|
599
|
+
4005: "kicked out",
|
|
600
|
+
4006: "user forbidden",
|
|
601
|
+
4007: "parse message failed",
|
|
602
|
+
4008: "server restart",
|
|
603
|
+
};
|
|
604
|
+
return reasons[code] || `unknown code ${code}`;
|
|
605
|
+
}
|
|
606
|
+
handleAuthError(code) {
|
|
607
|
+
console.log("[websocket]handleAuthError", code);
|
|
608
|
+
if ([
|
|
609
|
+
AuthSocketError.AuthCodeBanned,
|
|
610
|
+
AuthSocketError.AuthCodeExpired,
|
|
611
|
+
AuthSocketError.AuthCodeFail,
|
|
612
|
+
AuthSocketError.AuthCodeNotActivated,
|
|
613
|
+
].includes(code)) {
|
|
614
|
+
this.events.emit("auth-error", { code });
|
|
615
|
+
this.clearAuth();
|
|
616
|
+
this.disconnect("auth_error");
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
on(key, fn) {
|
|
620
|
+
this.events.on(key, fn);
|
|
621
|
+
}
|
|
622
|
+
emit(key, args) {
|
|
623
|
+
this.events.emit(key, args);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
export let Socket;
|
|
627
|
+
export { _Socket };
|
|
628
|
+
export function getSocket() {
|
|
629
|
+
if (!Socket) {
|
|
630
|
+
Socket = new _Socket();
|
|
631
|
+
}
|
|
632
|
+
return Socket;
|
|
633
|
+
}
|
|
634
|
+
export function setSocket(socket) {
|
|
635
|
+
Socket = socket;
|
|
636
|
+
}
|
|
637
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export declare const BotMsgSocketClient: import("./connect").PromiseClient<{
|
|
2
|
+
readonly typeName: "step.capy.botmsg.BotMsg";
|
|
3
|
+
readonly methods: {
|
|
4
|
+
readonly fetchMessages: {
|
|
5
|
+
readonly name: "FetchMessages";
|
|
6
|
+
readonly I: typeof import("../proto/capy/botmsg/botmsg_pb").FetchMessagesRequest;
|
|
7
|
+
readonly O: typeof import("../proto/capy/botmsg/botmsg_pb").FetchMessagesResponse;
|
|
8
|
+
readonly kind: import("@bufbuild/protobuf").MethodKind.Unary;
|
|
9
|
+
};
|
|
10
|
+
readonly hasNewMessages: {
|
|
11
|
+
readonly name: "HasNewMessages";
|
|
12
|
+
readonly I: typeof import("../proto/capy/botmsg/botmsg_pb").HasNewMessagesRequest;
|
|
13
|
+
readonly O: typeof import("../proto/capy/botmsg/botmsg_pb").HasNewMessagesResponse;
|
|
14
|
+
readonly kind: import("@bufbuild/protobuf").MethodKind.Unary;
|
|
15
|
+
};
|
|
16
|
+
readonly sendMessages: {
|
|
17
|
+
readonly name: "SendMessages";
|
|
18
|
+
readonly I: typeof import("../proto/capy/botmsg/botmsg_pb").SendMessagesRequest;
|
|
19
|
+
readonly O: typeof import("../proto/capy/botmsg/botmsg_pb").SendMessagesResponse;
|
|
20
|
+
readonly kind: import("@bufbuild/protobuf").MethodKind.Unary;
|
|
21
|
+
};
|
|
22
|
+
readonly pushMessages: {
|
|
23
|
+
readonly name: "PushMessages";
|
|
24
|
+
readonly I: typeof import("../proto/capy/botmsg/botmsg_pb").PushMessagesRequest;
|
|
25
|
+
readonly O: typeof import("../proto/capy/botmsg/botmsg_pb").PushMessagesResponse;
|
|
26
|
+
readonly kind: import("@bufbuild/protobuf").MethodKind.Unary;
|
|
27
|
+
};
|
|
28
|
+
readonly checkBotOnline: {
|
|
29
|
+
readonly name: "CheckBotOnline";
|
|
30
|
+
readonly I: typeof import("../proto/capy/botmsg/botmsg_pb").CheckBotOnlineRequest;
|
|
31
|
+
readonly O: typeof import("../proto/capy/botmsg/botmsg_pb").CheckBotOnlineResponse;
|
|
32
|
+
readonly kind: import("@bufbuild/protobuf").MethodKind.Unary;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
}>;
|
|
36
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SocketCallbacks } from './connect';
|
|
2
|
+
type StreamFuncs<I, O> = {
|
|
3
|
+
start: (cb?: SocketCallbacks<O>['onStart']) => StreamFuncs<I, O>;
|
|
4
|
+
on: (cb?: SocketCallbacks<O>['onData']) => StreamFuncs<I, O>;
|
|
5
|
+
do: () => Promise<O>;
|
|
6
|
+
error: (cb?: SocketCallbacks<O>['onError']) => StreamFuncs<I, O>;
|
|
7
|
+
};
|
|
8
|
+
export declare const stream: <I, O>(method: (req: I, callbacks?: SocketCallbacks<O>, once?: boolean) => Promise<O>, payload: I) => StreamFuncs<I, O>;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=stream.d.ts.map
|