@kevisual/cnb 0.0.25 → 0.0.27
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/keep.d.ts +2 -0
- package/dist/keep.js +2947 -32
- package/dist/opencode.js +271 -155
- package/dist/routes.d.ts +19 -0
- package/dist/routes.js +270 -154
- package/package.json +3 -3
- package/src/cnb-core.ts +1 -1
- package/src/issue/index.ts +59 -0
- package/src/issue/issue-alive/issue-alive.md +24 -0
- package/src/issue/issue-alive/issues-alive.html +2 -0
- package/src/issue/issue-alive.ts +15 -0
- package/src/workspace/keep-live.ts +91 -31
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
// WebSocket Keep-Alive Client Library
|
|
2
|
-
|
|
2
|
+
// 运行时检测:Bun 使用原生 WebSocket,Node.js 使用 ws 库
|
|
3
|
+
let WebSocketModule: any;
|
|
4
|
+
|
|
5
|
+
if (typeof Bun !== 'undefined') {
|
|
6
|
+
// Bun 环境:使用原生 WebSocket
|
|
7
|
+
WebSocketModule = { WebSocket: globalThis.WebSocket };
|
|
8
|
+
} else {
|
|
9
|
+
// Node.js 环境:使用 ws 库
|
|
10
|
+
WebSocketModule = await import('ws');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const WebSocket = WebSocketModule.WebSocket;
|
|
3
14
|
|
|
4
15
|
export interface KeepAliveConfig {
|
|
5
16
|
wsUrl: string;
|
|
@@ -31,6 +42,7 @@ export class WSKeepAlive {
|
|
|
31
42
|
private pingTimer: NodeJS.Timeout | null = null;
|
|
32
43
|
private messageHandlers: Set<MessageHandler> = new Set();
|
|
33
44
|
private url: URL;
|
|
45
|
+
private readonly isBun: boolean;
|
|
34
46
|
|
|
35
47
|
constructor(config: KeepAliveConfig) {
|
|
36
48
|
this.config = {
|
|
@@ -48,6 +60,7 @@ export class WSKeepAlive {
|
|
|
48
60
|
debug: config.debug ?? false,
|
|
49
61
|
};
|
|
50
62
|
this.url = new URL(this.config.wsUrl);
|
|
63
|
+
this.isBun = typeof Bun !== 'undefined';
|
|
51
64
|
}
|
|
52
65
|
|
|
53
66
|
private log(message: string) {
|
|
@@ -107,44 +120,91 @@ export class WSKeepAlive {
|
|
|
107
120
|
}
|
|
108
121
|
});
|
|
109
122
|
|
|
110
|
-
this.
|
|
111
|
-
|
|
112
|
-
this.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
123
|
+
if (this.isBun) {
|
|
124
|
+
// Bun 环境:使用标准 Web API
|
|
125
|
+
const ws = this.ws as any;
|
|
126
|
+
ws.onopen = () => {
|
|
127
|
+
debug && this.log("Connected!");
|
|
128
|
+
this.reconnectAttempts = 0;
|
|
129
|
+
this.config.onConnect();
|
|
130
|
+
this.startPing();
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
ws.onmessage = async (event: MessageEvent) => {
|
|
134
|
+
let data: Buffer | string;
|
|
135
|
+
|
|
136
|
+
if (event.data instanceof Blob) {
|
|
137
|
+
data = Buffer.from(await event.data.arrayBuffer());
|
|
138
|
+
} else if (event.data instanceof ArrayBuffer) {
|
|
139
|
+
data = Buffer.from(event.data);
|
|
140
|
+
} else if (typeof event.data === 'string') {
|
|
141
|
+
data = event.data;
|
|
142
|
+
} else {
|
|
143
|
+
data = Buffer.from(event.data);
|
|
144
|
+
}
|
|
129
145
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
146
|
+
this.handleMessage(data);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
ws.onclose = (event: CloseEvent) => {
|
|
150
|
+
debug && this.log(`Disconnected (code: ${event.code})`);
|
|
151
|
+
this.stopPing();
|
|
152
|
+
this.config.onDisconnect(event.code);
|
|
153
|
+
this.handleReconnect();
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
ws.onerror = (event: Event) => {
|
|
157
|
+
debug && this.log(`Error: ${event}`);
|
|
158
|
+
this.config.onError(new Error("WebSocket error"));
|
|
159
|
+
};
|
|
160
|
+
} else {
|
|
161
|
+
// Node.js (ws 库):使用 EventEmitter 模式
|
|
162
|
+
const ws = this.ws as any;
|
|
163
|
+
ws.on("open", () => {
|
|
164
|
+
debug && this.log("Connected!");
|
|
165
|
+
this.reconnectAttempts = 0;
|
|
166
|
+
this.config.onConnect();
|
|
167
|
+
this.startPing();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
ws.on("message", (data: any) => {
|
|
171
|
+
this.handleMessage(data);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
ws.on("close", (code: number) => {
|
|
175
|
+
debug && this.log(`Disconnected (code: ${code})`);
|
|
176
|
+
this.stopPing();
|
|
177
|
+
this.config.onDisconnect(code);
|
|
178
|
+
this.handleReconnect();
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
ws.on("error", (err: Error) => {
|
|
182
|
+
debug && this.log(`Error: ${err.message}`);
|
|
183
|
+
this.config.onError(err);
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
136
187
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
188
|
+
// 统一的消息处理方法
|
|
189
|
+
private handleMessage(data: Buffer | string) {
|
|
190
|
+
if (Buffer.isBuffer(data)) {
|
|
191
|
+
const parsed = this.parseMessage(data);
|
|
192
|
+
this.config.onMessage(parsed?.raw ?? data);
|
|
193
|
+
|
|
194
|
+
this.messageHandlers.forEach(handler => {
|
|
195
|
+
if (parsed) handler(parsed);
|
|
196
|
+
});
|
|
197
|
+
} else {
|
|
198
|
+
this.config.onMessage(data);
|
|
199
|
+
}
|
|
141
200
|
}
|
|
142
201
|
|
|
143
202
|
private startPing() {
|
|
144
203
|
this.stopPing();
|
|
145
204
|
this.pingTimer = setInterval(() => {
|
|
146
205
|
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
147
|
-
|
|
206
|
+
// 使用 JSON 格式的 ping 消息,兼容 Bun 和 Node.js
|
|
207
|
+
this.ws.send(JSON.stringify({ type: "ping", timestamp: Date.now() }));
|
|
148
208
|
this.log("Sent ping");
|
|
149
209
|
}
|
|
150
210
|
}, this.config.pingInterval);
|