@kaitranntt/ccs 7.69.1-dev.1 → 7.69.1-dev.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.
@@ -1,6 +1,34 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const { WebSocket } = require('ws');
3
+ function loadWebSocketImplementation() {
4
+ if (typeof globalThis.WebSocket === 'function') {
5
+ return globalThis.WebSocket;
6
+ }
7
+
8
+ try {
9
+ const { WebSocket } = require('undici');
10
+ if (typeof WebSocket === 'function') {
11
+ return WebSocket;
12
+ }
13
+ } catch {
14
+ // Fall through to the legacy ws dependency when available.
15
+ }
16
+
17
+ try {
18
+ const { WebSocket } = require('ws');
19
+ if (typeof WebSocket === 'function') {
20
+ return WebSocket;
21
+ }
22
+ } catch {
23
+ // Surface a dedicated error below if no implementation is available.
24
+ }
25
+
26
+ throw new Error(
27
+ 'Browser MCP could not find a WebSocket implementation. Tried globalThis.WebSocket, undici, and ws.'
28
+ );
29
+ }
30
+
31
+ const WebSocket = loadWebSocketImplementation();
4
32
 
5
33
  const PROTOCOL_VERSION = '2024-11-05';
6
34
  const SERVER_NAME = 'ccs-browser';
@@ -29,6 +57,66 @@ const NAVIGATION_POLL_INTERVAL_MS = 100;
29
57
  let inputBuffer = Buffer.alloc(0);
30
58
  let requestCounter = 0;
31
59
 
60
+ function addSocketListener(socket, eventName, handler) {
61
+ if (typeof socket.addEventListener === 'function') {
62
+ socket.addEventListener(eventName, handler);
63
+ return;
64
+ }
65
+
66
+ if (typeof socket.on === 'function') {
67
+ socket.on(eventName, handler);
68
+ }
69
+ }
70
+
71
+ async function getSocketMessageText(message) {
72
+ const data = message && typeof message === 'object' && 'data' in message ? message.data : message;
73
+
74
+ if (typeof data === 'string') {
75
+ return data;
76
+ }
77
+
78
+ if (Buffer.isBuffer(data)) {
79
+ return data.toString('utf8');
80
+ }
81
+
82
+ if (data instanceof ArrayBuffer) {
83
+ return Buffer.from(data).toString('utf8');
84
+ }
85
+
86
+ if (ArrayBuffer.isView(data)) {
87
+ return Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString('utf8');
88
+ }
89
+
90
+ if (data && typeof data.text === 'function') {
91
+ return await data.text();
92
+ }
93
+
94
+ return String(data);
95
+ }
96
+
97
+ function closeSocket(socket) {
98
+ if (typeof socket.close === 'function') {
99
+ socket.close();
100
+ }
101
+ }
102
+
103
+ function abortSocket(socket) {
104
+ if (typeof socket.terminate === 'function') {
105
+ socket.terminate();
106
+ return;
107
+ }
108
+
109
+ closeSocket(socket);
110
+ }
111
+
112
+ function toSocketError(error) {
113
+ if (error instanceof Error) {
114
+ return error;
115
+ }
116
+
117
+ return new Error('Browser MCP lost the DevTools websocket connection.');
118
+ }
119
+
32
120
  function shouldExposeTools() {
33
121
  return Boolean(process.env.CCS_BROWSER_DEVTOOLS_HTTP_URL);
34
122
  }
@@ -298,7 +386,7 @@ async function sendCdpCommand(page, method, params = {}) {
298
386
  const timer = setTimeout(() => {
299
387
  if (!settled) {
300
388
  settled = true;
301
- ws.terminate();
389
+ abortSocket(ws);
302
390
  reject(new Error('Browser MCP timed out waiting for a DevTools response.'));
303
391
  }
304
392
  }, CDP_TIMEOUT_MS);
@@ -309,10 +397,10 @@ async function sendCdpCommand(page, method, params = {}) {
309
397
  }
310
398
  clearTimeout(timer);
311
399
  settled = true;
312
- reject(error);
400
+ reject(toSocketError(error));
313
401
  }
314
402
 
315
- ws.on('open', () => {
403
+ addSocketListener(ws, 'open', () => {
316
404
  ws.send(
317
405
  JSON.stringify({
318
406
  id: requestId,
@@ -322,39 +410,43 @@ async function sendCdpCommand(page, method, params = {}) {
322
410
  );
323
411
  });
324
412
 
325
- ws.on('message', (data) => {
326
- if (settled) {
327
- return;
328
- }
413
+ addSocketListener(ws, 'message', (data) => {
414
+ void (async () => {
415
+ const raw = await getSocketMessageText(data);
329
416
 
330
- let message;
331
- try {
332
- message = JSON.parse(data.toString());
333
- } catch {
334
- return;
335
- }
417
+ if (settled) {
418
+ return;
419
+ }
336
420
 
337
- if (message.id !== requestId) {
338
- return;
339
- }
421
+ let message;
422
+ try {
423
+ message = JSON.parse(raw);
424
+ } catch {
425
+ return;
426
+ }
340
427
 
341
- clearTimeout(timer);
342
- settled = true;
343
- ws.close();
428
+ if (message.id !== requestId) {
429
+ return;
430
+ }
344
431
 
345
- if (message.error) {
346
- reject(new Error(message.error.message || 'DevTools request failed.'));
347
- return;
348
- }
432
+ clearTimeout(timer);
433
+ settled = true;
434
+ closeSocket(ws);
435
+
436
+ if (message.error) {
437
+ reject(new Error(message.error.message || 'DevTools request failed.'));
438
+ return;
439
+ }
349
440
 
350
- resolve(message.result || null);
441
+ resolve(message.result || null);
442
+ })().catch(settleError);
351
443
  });
352
444
 
353
- ws.on('error', (error) => {
445
+ addSocketListener(ws, 'error', (error) => {
354
446
  settleError(error);
355
447
  });
356
448
 
357
- ws.on('close', () => {
449
+ addSocketListener(ws, 'close', () => {
358
450
  if (settled) {
359
451
  return;
360
452
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaitranntt/ccs",
3
- "version": "7.69.1-dev.1",
3
+ "version": "7.69.1-dev.2",
4
4
  "description": "Claude Code Switch - Instant profile switching between Claude, GLM, Kimi, and more",
5
5
  "keywords": [
6
6
  "cli",