@kevisual/cnb 0.0.7 → 0.0.10
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/agent/routes/cnb-env/vscode.ts +2 -0
- package/dist/keep.d.ts +44 -0
- package/dist/keep.js +148 -0
- package/dist/opencode.js +6 -6
- package/package.json +6 -3
- package/src/keep.ts +1 -0
- package/src/workspace/keep-live.ts +187 -0
package/dist/keep.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
interface KeepAliveConfig {
|
|
2
|
+
wsUrl: string;
|
|
3
|
+
cookie: string;
|
|
4
|
+
reconnectInterval?: number;
|
|
5
|
+
maxReconnectAttempts?: number;
|
|
6
|
+
pingInterval?: number;
|
|
7
|
+
onMessage?: (data: Buffer | string) => void;
|
|
8
|
+
onConnect?: () => void;
|
|
9
|
+
onDisconnect?: (code: number) => void;
|
|
10
|
+
onError?: (error: Error) => void;
|
|
11
|
+
onSign?: (data: {
|
|
12
|
+
type: string;
|
|
13
|
+
data: string;
|
|
14
|
+
signedData: string;
|
|
15
|
+
}) => void;
|
|
16
|
+
debug?: boolean;
|
|
17
|
+
}
|
|
18
|
+
interface ParsedMessage {
|
|
19
|
+
type: string;
|
|
20
|
+
raw: Buffer;
|
|
21
|
+
payload?: any;
|
|
22
|
+
}
|
|
23
|
+
type MessageHandler = (msg: ParsedMessage) => void;
|
|
24
|
+
declare class WSKeepAlive {
|
|
25
|
+
private ws;
|
|
26
|
+
private config;
|
|
27
|
+
private reconnectAttempts;
|
|
28
|
+
private pingTimer;
|
|
29
|
+
private messageHandlers;
|
|
30
|
+
private url;
|
|
31
|
+
constructor(config: KeepAliveConfig);
|
|
32
|
+
private log;
|
|
33
|
+
private parseMessage;
|
|
34
|
+
connect(): void;
|
|
35
|
+
private startPing;
|
|
36
|
+
private stopPing;
|
|
37
|
+
private handleReconnect;
|
|
38
|
+
onMessage(handler: MessageHandler): () => boolean;
|
|
39
|
+
disconnect(): void;
|
|
40
|
+
}
|
|
41
|
+
declare function createKeepAlive(config: KeepAliveConfig): WSKeepAlive;
|
|
42
|
+
|
|
43
|
+
export { WSKeepAlive, createKeepAlive };
|
|
44
|
+
export type { KeepAliveConfig, ParsedMessage };
|
package/dist/keep.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// src/workspace/keep-live.ts
|
|
2
|
+
import WebSocket from "ws";
|
|
3
|
+
|
|
4
|
+
class WSKeepAlive {
|
|
5
|
+
ws = null;
|
|
6
|
+
config;
|
|
7
|
+
reconnectAttempts = 0;
|
|
8
|
+
pingTimer = null;
|
|
9
|
+
messageHandlers = new Set;
|
|
10
|
+
url;
|
|
11
|
+
constructor(config) {
|
|
12
|
+
this.config = {
|
|
13
|
+
wsUrl: config.wsUrl,
|
|
14
|
+
cookie: config.cookie,
|
|
15
|
+
reconnectInterval: config.reconnectInterval ?? 5000,
|
|
16
|
+
maxReconnectAttempts: config.maxReconnectAttempts ?? 3,
|
|
17
|
+
pingInterval: config.pingInterval ?? 30000,
|
|
18
|
+
onMessage: config.onMessage ?? (() => {}),
|
|
19
|
+
onConnect: config.onConnect ?? (() => {}),
|
|
20
|
+
onDisconnect: config.onDisconnect ?? (() => {}),
|
|
21
|
+
onError: config.onError ?? (() => {}),
|
|
22
|
+
onSign: config.onSign ?? (() => {}),
|
|
23
|
+
debug: config.debug ?? false
|
|
24
|
+
};
|
|
25
|
+
this.url = new URL(this.config.wsUrl);
|
|
26
|
+
}
|
|
27
|
+
log(message) {
|
|
28
|
+
if (!this.config.debug)
|
|
29
|
+
return;
|
|
30
|
+
const timestamp = new Date().toISOString();
|
|
31
|
+
const msg = `[${timestamp}] ${message}`;
|
|
32
|
+
console.log(msg);
|
|
33
|
+
}
|
|
34
|
+
parseMessage(data) {
|
|
35
|
+
const result = { type: "unknown", raw: data };
|
|
36
|
+
if (data.length < 14) {
|
|
37
|
+
result.type = "raw";
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
const prefix = data.slice(0, 13);
|
|
41
|
+
const msgType = prefix[0];
|
|
42
|
+
const jsonStart = data.indexOf(113);
|
|
43
|
+
if (jsonStart !== -1) {
|
|
44
|
+
try {
|
|
45
|
+
const jsonStr = data.slice(jsonStart + 1).toString();
|
|
46
|
+
const payload = JSON.parse(jsonStr);
|
|
47
|
+
result.type = `binary(0x${msgType.toString(16)})`;
|
|
48
|
+
result.payload = payload;
|
|
49
|
+
if (payload.type === "sign" && this.config.onSign) {
|
|
50
|
+
this.config.onSign(payload);
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
} catch {
|
|
54
|
+
result.type = "binary(json-parse-error)";
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
result.type = "raw";
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
connect() {
|
|
62
|
+
const { wsUrl, cookie, debug } = this.config;
|
|
63
|
+
this.log(`Connecting to ${wsUrl}...`);
|
|
64
|
+
this.ws = new WebSocket(wsUrl, {
|
|
65
|
+
headers: {
|
|
66
|
+
Origin: this.url.origin,
|
|
67
|
+
Cookie: cookie,
|
|
68
|
+
"Cache-Control": "no-cache",
|
|
69
|
+
"Accept-Language": "zh-CN,zh;q=0.9",
|
|
70
|
+
Pragma: "no-cache",
|
|
71
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
|
72
|
+
"Sec-WebSocket-Extensions": "permessage-deflate"
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
this.ws.on("open", () => {
|
|
76
|
+
debug && this.log("Connected!");
|
|
77
|
+
this.reconnectAttempts = 0;
|
|
78
|
+
this.config.onConnect();
|
|
79
|
+
this.startPing();
|
|
80
|
+
});
|
|
81
|
+
this.ws.on("message", (data) => {
|
|
82
|
+
if (Buffer.isBuffer(data)) {
|
|
83
|
+
const parsed = this.parseMessage(data);
|
|
84
|
+
this.config.onMessage(parsed?.raw ?? data);
|
|
85
|
+
this.messageHandlers.forEach((handler) => {
|
|
86
|
+
if (parsed)
|
|
87
|
+
handler(parsed);
|
|
88
|
+
});
|
|
89
|
+
} else {
|
|
90
|
+
this.config.onMessage(data);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
this.ws.on("close", (code) => {
|
|
94
|
+
debug && this.log(`Disconnected (code: ${code})`);
|
|
95
|
+
this.stopPing();
|
|
96
|
+
this.config.onDisconnect(code);
|
|
97
|
+
this.handleReconnect();
|
|
98
|
+
});
|
|
99
|
+
this.ws.on("error", (err) => {
|
|
100
|
+
debug && this.log(`Error: ${err.message}`);
|
|
101
|
+
this.config.onError(err);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
startPing() {
|
|
105
|
+
this.stopPing();
|
|
106
|
+
this.pingTimer = setInterval(() => {
|
|
107
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
108
|
+
this.ws.ping();
|
|
109
|
+
this.log("Sent ping");
|
|
110
|
+
}
|
|
111
|
+
}, this.config.pingInterval);
|
|
112
|
+
}
|
|
113
|
+
stopPing() {
|
|
114
|
+
if (this.pingTimer) {
|
|
115
|
+
clearInterval(this.pingTimer);
|
|
116
|
+
this.pingTimer = null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
handleReconnect() {
|
|
120
|
+
if (this.reconnectAttempts >= this.config.maxReconnectAttempts) {
|
|
121
|
+
this.log(`Max reconnect attempts (${this.config.maxReconnectAttempts}) reached. Giving up.`);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
this.reconnectAttempts++;
|
|
125
|
+
this.log(`Reconnecting in ${this.config.reconnectInterval}ms... (attempt ${this.reconnectAttempts}/${this.config.maxReconnectAttempts})`);
|
|
126
|
+
setTimeout(() => this.connect(), this.config.reconnectInterval);
|
|
127
|
+
}
|
|
128
|
+
onMessage(handler) {
|
|
129
|
+
this.messageHandlers.add(handler);
|
|
130
|
+
return () => this.messageHandlers.delete(handler);
|
|
131
|
+
}
|
|
132
|
+
disconnect() {
|
|
133
|
+
this.stopPing();
|
|
134
|
+
if (this.ws) {
|
|
135
|
+
this.ws.close();
|
|
136
|
+
this.ws = null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function createKeepAlive(config) {
|
|
141
|
+
const client = new WSKeepAlive(config);
|
|
142
|
+
client.connect();
|
|
143
|
+
return client;
|
|
144
|
+
}
|
|
145
|
+
export {
|
|
146
|
+
createKeepAlive,
|
|
147
|
+
WSKeepAlive
|
|
148
|
+
};
|
package/dist/opencode.js
CHANGED
|
@@ -17243,7 +17243,7 @@ function requireStream() {
|
|
|
17243
17243
|
if (!duplex.push(data))
|
|
17244
17244
|
ws.pause();
|
|
17245
17245
|
});
|
|
17246
|
-
ws.once("error", function
|
|
17246
|
+
ws.once("error", function error2(err) {
|
|
17247
17247
|
if (duplex.destroyed)
|
|
17248
17248
|
return;
|
|
17249
17249
|
terminateOnDestroy = false;
|
|
@@ -17261,7 +17261,7 @@ function requireStream() {
|
|
|
17261
17261
|
return;
|
|
17262
17262
|
}
|
|
17263
17263
|
let called = false;
|
|
17264
|
-
ws.once("error", function
|
|
17264
|
+
ws.once("error", function error2(err2) {
|
|
17265
17265
|
called = true;
|
|
17266
17266
|
callback(err2);
|
|
17267
17267
|
});
|
|
@@ -32161,7 +32161,7 @@ function date5(params) {
|
|
|
32161
32161
|
|
|
32162
32162
|
// node_modules/.pnpm/zod@4.1.8/node_modules/zod/v4/classic/external.js
|
|
32163
32163
|
config3(en_default());
|
|
32164
|
-
// node_modules/.pnpm/@opencode-ai+plugin@1.1.
|
|
32164
|
+
// node_modules/.pnpm/@opencode-ai+plugin@1.1.44/node_modules/@opencode-ai/plugin/dist/tool.js
|
|
32165
32165
|
function tool2(input) {
|
|
32166
32166
|
return input;
|
|
32167
32167
|
}
|
|
@@ -52972,7 +52972,7 @@ var init_crypto = __esm(() => {
|
|
|
52972
52972
|
} catch (e) {
|
|
52973
52973
|
errorProto = getProto(getProto(e)), INTRINSICS["%Error.prototype%"] = errorProto;
|
|
52974
52974
|
}
|
|
52975
|
-
var errorProto, doEval = function
|
|
52975
|
+
var errorProto, doEval = function doEval2(name) {
|
|
52976
52976
|
var value;
|
|
52977
52977
|
if (name === "%AsyncFunction%")
|
|
52978
52978
|
value = getEvalledConstructor("async function () {}");
|
|
@@ -52981,11 +52981,11 @@ var init_crypto = __esm(() => {
|
|
|
52981
52981
|
else if (name === "%AsyncGeneratorFunction%")
|
|
52982
52982
|
value = getEvalledConstructor("async function* () {}");
|
|
52983
52983
|
else if (name === "%AsyncGenerator%") {
|
|
52984
|
-
var fn =
|
|
52984
|
+
var fn = doEval2("%AsyncGeneratorFunction%");
|
|
52985
52985
|
if (fn)
|
|
52986
52986
|
value = fn.prototype;
|
|
52987
52987
|
} else if (name === "%AsyncIteratorPrototype%") {
|
|
52988
|
-
var gen =
|
|
52988
|
+
var gen = doEval2("%AsyncGenerator%");
|
|
52989
52989
|
if (gen && getProto)
|
|
52990
52990
|
value = getProto(gen.prototype);
|
|
52991
52991
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kevisual/cnb",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -21,9 +21,10 @@
|
|
|
21
21
|
"@kevisual/ai": "^0.0.24",
|
|
22
22
|
"@kevisual/context": "^0.0.4",
|
|
23
23
|
"@kevisual/types": "^0.0.12",
|
|
24
|
-
"@opencode-ai/plugin": "^1.1.
|
|
25
|
-
"@types/bun": "^1.3.
|
|
24
|
+
"@opencode-ai/plugin": "^1.1.44",
|
|
25
|
+
"@types/bun": "^1.3.8",
|
|
26
26
|
"@types/node": "^25.1.0",
|
|
27
|
+
"@types/ws": "^8.18.1",
|
|
27
28
|
"dotenv": "^17.2.3"
|
|
28
29
|
},
|
|
29
30
|
"publishConfig": {
|
|
@@ -35,11 +36,13 @@
|
|
|
35
36
|
"@kevisual/use-config": "^1.0.28",
|
|
36
37
|
"es-toolkit": "^1.44.0",
|
|
37
38
|
"nanoid": "^5.1.6",
|
|
39
|
+
"ws": "npm:@kevisual/ws",
|
|
38
40
|
"zod": "^4.3.6"
|
|
39
41
|
},
|
|
40
42
|
"exports": {
|
|
41
43
|
".": "./mod.ts",
|
|
42
44
|
"./opencode": "./dist/opencode.js",
|
|
45
|
+
"./keep": "./dist/keep.js",
|
|
43
46
|
"./src/*": "./src/*",
|
|
44
47
|
"./agent/*": "./agent/*"
|
|
45
48
|
}
|
package/src/keep.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './workspace/keep-live.ts';
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// WebSocket Keep-Alive Client Library
|
|
2
|
+
import WebSocket from "ws";
|
|
3
|
+
|
|
4
|
+
export interface KeepAliveConfig {
|
|
5
|
+
wsUrl: string;
|
|
6
|
+
cookie: string;
|
|
7
|
+
reconnectInterval?: number;
|
|
8
|
+
maxReconnectAttempts?: number;
|
|
9
|
+
pingInterval?: number;
|
|
10
|
+
onMessage?: (data: Buffer | string) => void;
|
|
11
|
+
onConnect?: () => void;
|
|
12
|
+
onDisconnect?: (code: number) => void;
|
|
13
|
+
onError?: (error: Error) => void;
|
|
14
|
+
onSign?: (data: { type: string; data: string; signedData: string }) => void;
|
|
15
|
+
debug?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ParsedMessage {
|
|
19
|
+
type: string;
|
|
20
|
+
raw: Buffer;
|
|
21
|
+
payload?: any;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type MessageHandler = (msg: ParsedMessage) => void;
|
|
25
|
+
|
|
26
|
+
export class WSKeepAlive {
|
|
27
|
+
private ws: WebSocket | null = null;
|
|
28
|
+
private config: Required<KeepAliveConfig>;
|
|
29
|
+
private reconnectAttempts = 0;
|
|
30
|
+
private pingTimer: NodeJS.Timeout | null = null;
|
|
31
|
+
private messageHandlers: Set<MessageHandler> = new Set();
|
|
32
|
+
private url: URL;
|
|
33
|
+
|
|
34
|
+
constructor(config: KeepAliveConfig) {
|
|
35
|
+
this.config = {
|
|
36
|
+
wsUrl: config.wsUrl,
|
|
37
|
+
cookie: config.cookie,
|
|
38
|
+
reconnectInterval: config.reconnectInterval ?? 5000,
|
|
39
|
+
maxReconnectAttempts: config.maxReconnectAttempts ?? 3,
|
|
40
|
+
pingInterval: config.pingInterval ?? 30000,
|
|
41
|
+
onMessage: config.onMessage ?? (() => {}),
|
|
42
|
+
onConnect: config.onConnect ?? (() => {}),
|
|
43
|
+
onDisconnect: config.onDisconnect ?? (() => {}),
|
|
44
|
+
onError: config.onError ?? (() => {}),
|
|
45
|
+
onSign: config.onSign ?? (() => {}),
|
|
46
|
+
debug: config.debug ?? false,
|
|
47
|
+
};
|
|
48
|
+
this.url = new URL(this.config.wsUrl);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private log(message: string) {
|
|
52
|
+
if (!this.config.debug) return;
|
|
53
|
+
const timestamp = new Date().toISOString();
|
|
54
|
+
const msg = `[${timestamp}] ${message}`;
|
|
55
|
+
console.log(msg);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private parseMessage(data: Buffer): ParsedMessage | null {
|
|
59
|
+
const result: ParsedMessage = { type: "unknown", raw: data };
|
|
60
|
+
|
|
61
|
+
if (data.length < 14) {
|
|
62
|
+
result.type = "raw";
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const prefix = data.slice(0, 13);
|
|
67
|
+
const msgType = prefix[0];
|
|
68
|
+
const jsonStart = data.indexOf(0x71); // 0x71 = 'q'
|
|
69
|
+
|
|
70
|
+
if (jsonStart !== -1) {
|
|
71
|
+
try {
|
|
72
|
+
const jsonStr = data.slice(jsonStart + 1).toString();
|
|
73
|
+
const payload = JSON.parse(jsonStr);
|
|
74
|
+
result.type = `binary(0x${msgType.toString(16)})`;
|
|
75
|
+
result.payload = payload;
|
|
76
|
+
|
|
77
|
+
// 特殊处理 sign 类型
|
|
78
|
+
if (payload.type === "sign" && this.config.onSign) {
|
|
79
|
+
this.config.onSign(payload);
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
} catch {
|
|
83
|
+
result.type = "binary(json-parse-error)";
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
result.type = "raw";
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
connect() {
|
|
93
|
+
const { wsUrl, cookie, debug } = this.config;
|
|
94
|
+
this.log(`Connecting to ${wsUrl}...`);
|
|
95
|
+
|
|
96
|
+
this.ws = new WebSocket(wsUrl, {
|
|
97
|
+
headers: {
|
|
98
|
+
"Origin": this.url.origin,
|
|
99
|
+
"Cookie": cookie,
|
|
100
|
+
"Cache-Control": "no-cache",
|
|
101
|
+
"Accept-Language": "zh-CN,zh;q=0.9",
|
|
102
|
+
"Pragma": "no-cache",
|
|
103
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
|
104
|
+
"Sec-WebSocket-Extensions": "permessage-deflate",
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
this.ws.on("open", () => {
|
|
109
|
+
debug && this.log("Connected!");
|
|
110
|
+
this.reconnectAttempts = 0;
|
|
111
|
+
this.config.onConnect();
|
|
112
|
+
this.startPing();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
this.ws.on("message", (data: any) => {
|
|
116
|
+
if (Buffer.isBuffer(data)) {
|
|
117
|
+
const parsed = this.parseMessage(data);
|
|
118
|
+
this.config.onMessage(parsed?.raw ?? data);
|
|
119
|
+
|
|
120
|
+
this.messageHandlers.forEach(handler => {
|
|
121
|
+
if (parsed) handler(parsed);
|
|
122
|
+
});
|
|
123
|
+
} else {
|
|
124
|
+
this.config.onMessage(data);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
this.ws.on("close", (code: number) => {
|
|
129
|
+
debug && this.log(`Disconnected (code: ${code})`);
|
|
130
|
+
this.stopPing();
|
|
131
|
+
this.config.onDisconnect(code);
|
|
132
|
+
this.handleReconnect();
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
this.ws.on("error", (err: Error) => {
|
|
136
|
+
debug && this.log(`Error: ${err.message}`);
|
|
137
|
+
this.config.onError(err);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private startPing() {
|
|
142
|
+
this.stopPing();
|
|
143
|
+
this.pingTimer = setInterval(() => {
|
|
144
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
145
|
+
this.ws.ping();
|
|
146
|
+
this.log("Sent ping");
|
|
147
|
+
}
|
|
148
|
+
}, this.config.pingInterval);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private stopPing() {
|
|
152
|
+
if (this.pingTimer) {
|
|
153
|
+
clearInterval(this.pingTimer);
|
|
154
|
+
this.pingTimer = null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
private handleReconnect() {
|
|
159
|
+
if (this.reconnectAttempts >= this.config.maxReconnectAttempts) {
|
|
160
|
+
this.log(`Max reconnect attempts (${this.config.maxReconnectAttempts}) reached. Giving up.`);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
this.reconnectAttempts++;
|
|
164
|
+
this.log(`Reconnecting in ${this.config.reconnectInterval}ms... (attempt ${this.reconnectAttempts}/${this.config.maxReconnectAttempts})`);
|
|
165
|
+
setTimeout(() => this.connect(), this.config.reconnectInterval);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
onMessage(handler: MessageHandler) {
|
|
169
|
+
this.messageHandlers.add(handler);
|
|
170
|
+
return () => this.messageHandlers.delete(handler);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
disconnect() {
|
|
174
|
+
this.stopPing();
|
|
175
|
+
if (this.ws) {
|
|
176
|
+
this.ws.close();
|
|
177
|
+
this.ws = null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// 便捷函数:快速创建并启动
|
|
183
|
+
export function createKeepAlive(config: KeepAliveConfig): WSKeepAlive {
|
|
184
|
+
const client = new WSKeepAlive(config);
|
|
185
|
+
client.connect();
|
|
186
|
+
return client;
|
|
187
|
+
}
|