@cloudbase/cloudbase-mcp 1.7.4 → 1.7.6

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.
@@ -17,13 +17,25 @@ export class InteractiveServer {
17
17
  sessionData = new Map();
18
18
  // 固定端口配置
19
19
  DEFAULT_PORT = 3721;
20
- FALLBACK_PORTS = [3722, 3723, 3724, 3725];
20
+ FALLBACK_PORTS = [3722, 3723, 3724, 3725, 3726, 3727, 3728, 3729, 3730, 3731, 3732, 3733, 3734, 3735];
21
21
  constructor() {
22
22
  this.app = express();
23
23
  this.server = http.createServer(this.app);
24
24
  this.wss = new WebSocketServer({ server: this.server });
25
25
  this.setupExpress();
26
26
  this.setupWebSocket();
27
+ // 确保进程退出时清理资源
28
+ process.on('exit', () => this.cleanup());
29
+ process.on('SIGINT', () => this.cleanup());
30
+ process.on('SIGTERM', () => this.cleanup());
31
+ }
32
+ cleanup() {
33
+ if (this.isRunning) {
34
+ debug('Cleaning up interactive server resources...');
35
+ this.server.close();
36
+ this.wss.close();
37
+ this.isRunning = false;
38
+ }
27
39
  }
28
40
  setupExpress() {
29
41
  this.app.use(express.json());
@@ -148,32 +160,41 @@ export class InteractiveServer {
148
160
  let currentIndex = 0;
149
161
  const tryNextPort = () => {
150
162
  if (currentIndex >= tryPorts.length) {
151
- const err = new Error('All ports are in use, failed to start server');
163
+ const err = new Error(`All ${tryPorts.length} ports are in use (${tryPorts.join(', ')}), failed to start server`);
152
164
  error('Server start failed', err);
153
165
  reject(err);
154
166
  return;
155
167
  }
156
168
  const portToTry = tryPorts[currentIndex];
157
169
  currentIndex++;
158
- debug(`Trying to start server on port ${portToTry}`);
159
- // 清除之前的错误监听器
170
+ debug(`Trying to start server on port ${portToTry} (attempt ${currentIndex}/${tryPorts.length})`);
171
+ // 清除之前的所有监听器
160
172
  this.server.removeAllListeners('error');
161
- this.server.on('error', (err) => {
173
+ this.server.removeAllListeners('listening');
174
+ // 设置错误处理
175
+ const errorHandler = (err) => {
162
176
  if (err.code === 'EADDRINUSE') {
163
177
  warn(`Port ${portToTry} is in use, trying next port...`);
178
+ // 清理当前尝试
179
+ this.server.removeAllListeners('error');
180
+ this.server.removeAllListeners('listening');
164
181
  tryNextPort();
165
182
  }
166
183
  else {
167
184
  error('Server error', err);
168
185
  reject(err);
169
186
  }
170
- });
171
- this.server.listen(portToTry, '127.0.0.1', () => {
187
+ };
188
+ // 设置成功监听处理
189
+ const listeningHandler = () => {
172
190
  const address = this.server.address();
173
191
  if (address && typeof address === 'object') {
174
192
  this.port = address.port;
175
193
  this.isRunning = true;
176
194
  info(`Interactive server started successfully on http://localhost:${this.port}`);
195
+ // 移除临时监听器
196
+ this.server.removeListener('error', errorHandler);
197
+ this.server.removeListener('listening', listeningHandler);
177
198
  resolve(this.port);
178
199
  }
179
200
  else {
@@ -181,19 +202,61 @@ export class InteractiveServer {
181
202
  error('Server start error', err);
182
203
  reject(err);
183
204
  }
184
- });
205
+ };
206
+ this.server.once('error', errorHandler);
207
+ this.server.once('listening', listeningHandler);
208
+ try {
209
+ this.server.listen(portToTry, '127.0.0.1');
210
+ }
211
+ catch (err) {
212
+ error(`Failed to bind to port ${portToTry}:`, err);
213
+ tryNextPort();
214
+ }
185
215
  };
186
216
  tryNextPort();
187
217
  });
188
218
  }
189
219
  async stop() {
190
- if (!this.isRunning)
220
+ if (!this.isRunning) {
221
+ debug('Interactive server is not running, nothing to stop');
191
222
  return;
192
- return new Promise((resolve) => {
193
- this.server.close(() => {
223
+ }
224
+ info('Stopping interactive server...');
225
+ return new Promise((resolve, reject) => {
226
+ // 设置超时,防止无限等待
227
+ const timeout = setTimeout(() => {
228
+ warn('Server close timeout, forcing cleanup');
194
229
  this.isRunning = false;
230
+ this.port = 0;
195
231
  resolve();
196
- });
232
+ }, 5000);
233
+ try {
234
+ // 首先关闭WebSocket服务器
235
+ this.wss.close(() => {
236
+ debug('WebSocket server closed');
237
+ });
238
+ // 然后关闭HTTP服务器
239
+ this.server.close((err) => {
240
+ clearTimeout(timeout);
241
+ if (err) {
242
+ error('Error closing server:', err);
243
+ reject(err);
244
+ }
245
+ else {
246
+ info('Interactive server stopped successfully');
247
+ this.isRunning = false;
248
+ this.port = 0;
249
+ resolve();
250
+ }
251
+ });
252
+ }
253
+ catch (err) {
254
+ clearTimeout(timeout);
255
+ error('Error stopping server:', err);
256
+ this.isRunning = false;
257
+ this.port = 0;
258
+ reject(err);
259
+ }
197
260
  });
198
261
  }
199
262
  async collectEnvId(availableEnvs) {
@@ -2438,6 +2501,14 @@ export class InteractiveServer {
2438
2501
  </body>
2439
2502
  </html>`;
2440
2503
  }
2504
+ // 公共方法获取运行状态
2505
+ get running() {
2506
+ return this.isRunning;
2507
+ }
2508
+ // 公共方法获取端口
2509
+ get currentPort() {
2510
+ return this.port;
2511
+ }
2441
2512
  }
2442
2513
  // 单例实例
2443
2514
  let interactiveServerInstance = null;
@@ -2447,3 +2518,27 @@ export function getInteractiveServer() {
2447
2518
  }
2448
2519
  return interactiveServerInstance;
2449
2520
  }
2521
+ export async function resetInteractiveServer() {
2522
+ if (interactiveServerInstance) {
2523
+ try {
2524
+ await interactiveServerInstance.stop();
2525
+ }
2526
+ catch (err) {
2527
+ error('Error stopping existing server instance:', err);
2528
+ }
2529
+ interactiveServerInstance = null;
2530
+ }
2531
+ }
2532
+ export async function getInteractiveServerSafe() {
2533
+ // 如果当前实例存在但不在运行状态,先清理
2534
+ if (interactiveServerInstance && !interactiveServerInstance.running) {
2535
+ try {
2536
+ await interactiveServerInstance.stop();
2537
+ }
2538
+ catch (err) {
2539
+ debug('Error stopping non-running server:', err);
2540
+ }
2541
+ interactiveServerInstance = null;
2542
+ }
2543
+ return getInteractiveServer();
2544
+ }