rcoder 1.0.0__py3-none-any.whl

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.
@@ -0,0 +1,958 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Rcoder核心模块(优化版本)
4
+ 针对低带宽场景和异步操作进行了优化
5
+ """
6
+
7
+ import ssl
8
+ import socket
9
+ import json
10
+ import time
11
+ import asyncio
12
+ import hashlib
13
+ import threading
14
+ import queue
15
+ import gzip
16
+ import zlib
17
+ from typing import Dict, Any, Optional, List, Tuple, Callable
18
+ from dataclasses import dataclass
19
+
20
+ @dataclass
21
+ class CommandResult:
22
+ """命令执行结果"""
23
+ stdout: str
24
+ stderr: str
25
+ returncode: int
26
+ execution_time: float
27
+
28
+ @dataclass
29
+ class BatchResult:
30
+ """批量命令执行结果"""
31
+ command: str
32
+ results: Dict[str, CommandResult]
33
+ success_count: int
34
+ failure_count: int
35
+ total_time: float
36
+
37
+ class RcoderCore:
38
+ """
39
+ Rcoder核心类(优化版本)
40
+ 针对低带宽场景和异步操作进行了优化
41
+ """
42
+
43
+ def __init__(self, host: str = '192.168.1.8', port: int = 443, use_https_disguise: bool = True,
44
+ proxy_server: Optional[Tuple[str, int]] = None, enable_compression: bool = True,
45
+ enable_connection_pool: bool = True, connection_pool_size: int = 5, password: Optional[str] = None):
46
+ """
47
+ 初始化Rcoder核心
48
+
49
+ Args:
50
+ host: 服务器主机
51
+ port: 服务器端口 (默认443,支持HTTPS伪装)
52
+ use_https_disguise: 是否使用HTTPS伪装
53
+ proxy_server: 中转服务器 (host, port),如 ('1.2.3.4', 443)
54
+ enable_compression: 是否启用数据压缩
55
+ enable_connection_pool: 是否启用连接池
56
+ connection_pool_size: 连接池大小
57
+ password: 认证密码
58
+ """
59
+ self.host = host
60
+ self.port = port
61
+ self.use_https_disguise = use_https_disguise
62
+ self.proxy_server = proxy_server
63
+ self.enable_compression = enable_compression
64
+ self.enable_connection_pool = enable_connection_pool
65
+ self.connection_pool_size = connection_pool_size
66
+ self.password = password
67
+ self.ssl_context = self._create_ssl_context()
68
+ self.token = None
69
+ self._monitoring_enabled = False
70
+ self._monitoring_thread = None
71
+ self._alert_queue = queue.Queue()
72
+ self._command_queue = queue.Queue()
73
+ self._results = {}
74
+ self._lock = threading.Lock()
75
+ self._session_id = hashlib.sha256(str(time.time()).encode()).hexdigest()
76
+
77
+ # 连接池相关
78
+ self._connection_pool = queue.Queue(maxsize=connection_pool_size)
79
+ self._pool_lock = threading.Lock()
80
+ self._connection_expiry = 300 # 连接过期时间(秒)
81
+ self._connection_times = {}
82
+
83
+ # 缓存相关
84
+ self._command_cache = {}
85
+ self._cache_expiry = 60 # 缓存过期时间(秒)
86
+
87
+ # 网络优化参数
88
+ self._timeout = 60
89
+ self._retry_delay = 0.5 # 初始重试延迟
90
+ self._max_retry_delay = 5 # 最大重试延迟
91
+
92
+ # 新增策略参数
93
+ self.enable_minimal_payload = False # 是否启用最小化负载
94
+ self.enable_exponential_backoff = False # 是否启用指数退避
95
+ self.enable_breakpoint_resume = False # 是否启用断点续传
96
+
97
+ config_info = f"{host}:{port} (HTTPS伪装: {'启用' if use_https_disguise else '禁用'})"
98
+ if proxy_server:
99
+ config_info += f" (中转服务器: {proxy_server[0]}:{proxy_server[1]})"
100
+
101
+ # 优化功能提示
102
+ optimizations = []
103
+ if enable_compression:
104
+ optimizations.append("数据压缩")
105
+ if enable_connection_pool:
106
+ optimizations.append("连接池")
107
+ if optimizations:
108
+ config_info += f" (优化: {', '.join(optimizations)})"
109
+
110
+ print(f"✅ Rcoder初始化完成 (会话ID: {self._session_id[:8]})]")
111
+ print(f"📡 连接配置: {config_info}")
112
+
113
+ def _create_ssl_context(self) -> ssl.SSLContext:
114
+ """创建SSL上下文,增强HTTPS伪装"""
115
+ context = ssl.create_default_context()
116
+
117
+ # 增强HTTPS伪装
118
+ if self.use_https_disguise:
119
+ # 模拟标准HTTPS客户端行为
120
+ context.minimum_version = ssl.TLSVersion.TLSv1_2
121
+ context.maximum_version = ssl.TLSVersion.TLSv1_3
122
+
123
+ # 禁用主机名检查以支持自定义端口
124
+ context.check_hostname = False
125
+ context.verify_mode = ssl.CERT_NONE
126
+
127
+ # 模拟常见浏览器的密码套件偏好
128
+ context.set_ciphers('ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384')
129
+ else:
130
+ # 标准模式
131
+ context.check_hostname = False
132
+ context.verify_mode = ssl.CERT_NONE
133
+
134
+ return context
135
+
136
+ def _get_connection(self):
137
+ """从连接池获取连接"""
138
+ if not self.enable_connection_pool:
139
+ return self._create_connection()
140
+
141
+ with self._pool_lock:
142
+ # 清理过期连接
143
+ current_time = time.time()
144
+ valid_connections = []
145
+ while not self._connection_pool.empty():
146
+ conn = self._connection_pool.get()
147
+ conn_id = id(conn)
148
+ if current_time - self._connection_times.get(conn_id, 0) < self._connection_expiry:
149
+ try:
150
+ # 测试连接是否有效
151
+ conn.send(b'')
152
+ valid_connections.append(conn)
153
+ except:
154
+ pass
155
+
156
+ # 将有效连接放回池
157
+ for conn in valid_connections:
158
+ if not self._connection_pool.full():
159
+ self._connection_pool.put(conn)
160
+
161
+ # 获取连接
162
+ if not self._connection_pool.empty():
163
+ conn = self._connection_pool.get()
164
+ self._connection_times[id(conn)] = current_time
165
+ return conn
166
+
167
+ # 没有可用连接,创建新连接
168
+ return self._create_connection()
169
+
170
+ def _create_connection(self):
171
+ """创建新连接"""
172
+ # 建立TCP连接(支持中转服务器)
173
+ if self.proxy_server:
174
+ sock = socket.create_connection((self.proxy_server[0], self.proxy_server[1]), timeout=self._timeout)
175
+
176
+ # 发送代理连接请求(简化版,减少数据传输)
177
+ proxy_connect = f"CONNECT {self.host}:{self.port} HTTP/1.1\r\n"
178
+ proxy_connect += f"Host: {self.host}:{self.port}\r\n"
179
+ proxy_connect += "Connection: Keep-Alive\r\n"
180
+ proxy_connect += "\r\n"
181
+
182
+ sock.send(proxy_connect.encode())
183
+ # 读取代理响应(丢弃)
184
+ sock.recv(4096)
185
+ else:
186
+ # 直接连接
187
+ sock = socket.create_connection((self.host, self.port), timeout=self._timeout)
188
+
189
+ # 增强HTTPS伪装:添加HTTP请求头模拟(简化版)
190
+ if self.use_https_disguise:
191
+ # 简化的HTTP伪装,减少数据传输
192
+ http_request = "GET / HTTP/1.1\r\n"
193
+ http_request += f"Host: {self.host}:{self.port}\r\n"
194
+ http_request += "User-Agent: Mozilla/5.0\r\n"
195
+ http_request += "Connection: keep-alive\r\n"
196
+ http_request += "\r\n"
197
+
198
+ sock.send(http_request.encode())
199
+ # 读取HTTP响应(丢弃)
200
+ sock.recv(4096)
201
+
202
+ # 包装为TLS连接
203
+ server_hostname = f"{self.host}" if self.use_https_disguise else "rcoder"
204
+ tls_sock = self.ssl_context.wrap_socket(sock, server_hostname=server_hostname)
205
+
206
+ return tls_sock
207
+
208
+ def _return_connection(self, conn):
209
+ """将连接放回连接池"""
210
+ if self.enable_connection_pool:
211
+ with self._pool_lock:
212
+ if not self._connection_pool.full():
213
+ try:
214
+ self._connection_pool.put(conn)
215
+ self._connection_times[id(conn)] = time.time()
216
+ return True
217
+ except:
218
+ pass
219
+
220
+ # 无法放回池,关闭连接
221
+ try:
222
+ conn.close()
223
+ except:
224
+ pass
225
+ return False
226
+
227
+ def _compress_data(self, data):
228
+ """压缩数据"""
229
+ if not self.enable_compression:
230
+ return data
231
+
232
+ try:
233
+ compressed = gzip.compress(data)
234
+ # 只有当压缩后数据更小时才使用压缩
235
+ if len(compressed) < len(data):
236
+ return compressed
237
+ except:
238
+ pass
239
+ return data
240
+
241
+ def _decompress_data(self, data):
242
+ """解压数据"""
243
+ if not self.enable_compression:
244
+ return data
245
+
246
+ try:
247
+ return gzip.decompress(data)
248
+ except:
249
+ return data
250
+
251
+ def _call(self, method: str, params: Dict[str, Any] = None,
252
+ retry_on_failure: bool = True, max_retries: int = 3) -> Dict[str, Any]:
253
+ """执行JSON-RPC调用,优化低带宽场景
254
+
255
+ Args:
256
+ method: RPC方法名
257
+ params: RPC参数
258
+ retry_on_failure: 是否在失败时重试
259
+ max_retries: 最大重试次数
260
+ """
261
+ retry_count = 0
262
+ current_delay = self._retry_delay
263
+
264
+ while retry_count <= max_retries:
265
+ conn = None
266
+ try:
267
+ # 获取连接
268
+ conn = self._get_connection()
269
+
270
+ # 构建请求
271
+ request = {
272
+ "jsonrpc": "2.0",
273
+ "id": int(time.time() * 1000),
274
+ "method": method,
275
+ "params": params or {},
276
+ "sid": self._session_id[:8], # 简化的session_id
277
+ "ts": int(time.time())
278
+ }
279
+
280
+ # 添加认证信息
281
+ if self.password:
282
+ request['auth'] = {
283
+ 'type': 'password',
284
+ 'password': self.password
285
+ }
286
+
287
+ # 最小化负载
288
+ if self.enable_minimal_payload:
289
+ # 移除可选字段
290
+ if "params" in request and not request["params"]:
291
+ del request["params"]
292
+
293
+ # 序列化请求
294
+ request_data = json.dumps(request, separators=(',', ':')).encode()
295
+
296
+ # 压缩数据
297
+ if self.enable_compression:
298
+ compressed_data = self._compress_data(request_data)
299
+ # 添加压缩标记
300
+ if len(compressed_data) < len(request_data):
301
+ request_data = b'COMPRESSED:' + compressed_data
302
+
303
+ # 发送请求
304
+ conn.send(request_data + b"\n")
305
+
306
+ # 接收响应(优化:使用循环接收,处理大数据)
307
+ response_data = b''
308
+ while True:
309
+ chunk = conn.recv(8192) # 增大接收缓冲区
310
+ if not chunk:
311
+ break
312
+ response_data += chunk
313
+ # 如果收到完整的JSON响应,提前结束
314
+ if b'\n' in response_data:
315
+ break
316
+
317
+ # 处理压缩响应
318
+ if response_data.startswith(b'COMPRESSED:'):
319
+ response_data = self._decompress_data(response_data[10:])
320
+
321
+ if not response_data:
322
+ if retry_on_failure and retry_count < max_retries:
323
+ retry_count += 1
324
+ # 指数退避
325
+ if self.enable_exponential_backoff:
326
+ current_delay = min(current_delay * (2 ** retry_count), self._max_retry_delay * 2)
327
+ else:
328
+ current_delay = min(current_delay * 1.5, self._max_retry_delay)
329
+ time.sleep(current_delay)
330
+ continue
331
+ raise Exception("Empty response from server")
332
+
333
+ # 解析响应
334
+ response = json.loads(response_data)
335
+
336
+ # 检查认证错误
337
+ if 'error' in response:
338
+ error_msg = response['error'].get('message', str(response['error']))
339
+ if 'auth' in error_msg.lower() or 'password' in error_msg.lower() or 'login' in error_msg.lower():
340
+ raise Exception(f"认证失败: {error_msg}. 请检查用户名和密码是否正确。")
341
+ raise Exception(f"服务器错误: {error_msg}")
342
+
343
+ return response
344
+
345
+ except (socket.error, ConnectionResetError, ConnectionRefusedError) as e:
346
+ error_msg = str(e)
347
+ if 'Connection refused' in error_msg or '10061' in error_msg:
348
+ if self.password:
349
+ raise Exception(f"连接失败: {error_msg}. 可能的原因: 服务器未运行、端口错误或认证失败。")
350
+ else:
351
+ raise Exception(f"连接失败: {error_msg}. 可能的原因: 服务器未运行或端口错误。")
352
+
353
+ if retry_on_failure and retry_count < max_retries:
354
+ retry_count += 1
355
+ # 指数退避
356
+ if self.enable_exponential_backoff:
357
+ current_delay = min(current_delay * (2 ** retry_count), self._max_retry_delay * 2)
358
+ else:
359
+ current_delay = min(current_delay * 1.5, self._max_retry_delay)
360
+ time.sleep(current_delay)
361
+ continue
362
+ raise
363
+ except json.JSONDecodeError as e:
364
+ if retry_on_failure and retry_count < max_retries:
365
+ retry_count += 1
366
+ # 指数退避
367
+ if self.enable_exponential_backoff:
368
+ current_delay = min(current_delay * (2 ** retry_count), self._max_retry_delay * 2)
369
+ else:
370
+ current_delay = min(current_delay * 1.5, self._max_retry_delay)
371
+ time.sleep(current_delay)
372
+ continue
373
+ raise
374
+ except Exception as e:
375
+ if retry_on_failure and retry_count < max_retries:
376
+ # 跳过认证错误的重试
377
+ if '认证失败' in str(e):
378
+ raise
379
+
380
+ retry_count += 1
381
+ # 指数退避
382
+ if self.enable_exponential_backoff:
383
+ current_delay = min(current_delay * (2 ** retry_count), self._max_retry_delay * 2)
384
+ else:
385
+ current_delay = min(current_delay * 1.5, self._max_retry_delay)
386
+ time.sleep(current_delay)
387
+ continue
388
+ raise
389
+ finally:
390
+ if conn:
391
+ self._return_connection(conn)
392
+
393
+ def _get_cache_key(self, method, params):
394
+ """生成缓存键"""
395
+ return hashlib.md5(f"{method}:{json.dumps(params, sort_keys=True)}".encode()).hexdigest()
396
+
397
+ def _get_cached_result(self, method, params):
398
+ """获取缓存的结果"""
399
+ cache_key = self._get_cache_key(method, params)
400
+ if cache_key in self._command_cache:
401
+ cached = self._command_cache[cache_key]
402
+ if time.time() - cached['timestamp'] < self._cache_expiry:
403
+ return cached['result']
404
+ # 缓存过期,删除
405
+ del self._command_cache[cache_key]
406
+ return None
407
+
408
+ def _set_cached_result(self, method, params, result):
409
+ """设置缓存的结果"""
410
+ cache_key = self._get_cache_key(method, params)
411
+ self._command_cache[cache_key] = {
412
+ 'result': result,
413
+ 'timestamp': time.time()
414
+ }
415
+
416
+ # 清理过期缓存
417
+ current_time = time.time()
418
+ expired_keys = [k for k, v in self._command_cache.items()
419
+ if current_time - v['timestamp'] >= self._cache_expiry]
420
+ for key in expired_keys:
421
+ del self._command_cache[key]
422
+
423
+ def execute(self, server: str, command: str, timeout: int = 60,
424
+ wait_for_restart: bool = False, restart_check_interval: int = 2,
425
+ restart_max_wait: int = 60, use_cache: bool = True) -> str:
426
+ """执行命令,优化低带宽场景
427
+
428
+ Args:
429
+ server: 服务器名称
430
+ command: 命令
431
+ timeout: 超时时间
432
+ wait_for_restart: 是否等待重启完成
433
+ restart_check_interval: 重启检查间隔
434
+ restart_max_wait: 最大重启等待时间
435
+ use_cache: 是否使用缓存
436
+ """
437
+ # 检查缓存
438
+ if use_cache:
439
+ cache_key = f"execute:{server}:{command}"
440
+ if cache_key in self._command_cache:
441
+ cached = self._command_cache[cache_key]
442
+ if time.time() - cached['timestamp'] < self._cache_expiry:
443
+ return cached['result']
444
+
445
+ # 检查是否为重启命令
446
+ is_restart_command = any(keyword in command.lower() for keyword in [
447
+ 'restart', 'systemctl restart', 'service restart', 'reboot'
448
+ ])
449
+
450
+ if is_restart_command and wait_for_restart:
451
+ print(f"执行重启命令并等待完成...")
452
+ print(f"命令: {command}")
453
+ print(f"最大等待时间: {restart_max_wait}秒")
454
+
455
+ try:
456
+ # 执行重启命令
457
+ result = self._call("tools/call", {
458
+ "name": "ssh_exec",
459
+ "arguments": {"name": server, "command": command, "timeout": timeout}
460
+ })
461
+
462
+ if "result" in result:
463
+ data = json.loads(result["result"]["content"][0]["text"])
464
+ print(f"重启命令执行结果: {data.get('stdout', '').strip() or data.get('stderr', '').strip()}")
465
+ except Exception as e:
466
+ print(f"重启命令执行异常: {e}")
467
+ # 继续等待,因为重启命令可能已经开始执行
468
+
469
+ # 等待重启完成
470
+ start_time = time.time()
471
+ elapsed = 0
472
+
473
+ print("等待服务重启...")
474
+ while elapsed < restart_max_wait:
475
+ time.sleep(restart_check_interval)
476
+ elapsed = time.time() - start_time
477
+
478
+ try:
479
+ # 尝试重新连接并检查服务是否可用(使用更简单的命令)
480
+ test_result = self._call(
481
+ "tools/call",
482
+ {
483
+ "name": "ssh_exec",
484
+ "arguments": {"name": server, "command": "echo 1", "timeout": 5}
485
+ },
486
+ retry_on_failure=True,
487
+ max_retries=1 # 减少重试次数,加快检测
488
+ )
489
+
490
+ if "result" in test_result:
491
+ print(f"✅ 服务已重启完成 (耗时: {elapsed:.1f}秒)")
492
+ result = f"重启完成 (耗时: {elapsed:.1f}秒)"
493
+ # 缓存结果
494
+ if use_cache:
495
+ self._command_cache[cache_key] = {
496
+ 'result': result,
497
+ 'timestamp': time.time()
498
+ }
499
+ return result
500
+
501
+ except (socket.error, ConnectionResetError, ConnectionRefusedError):
502
+ # 重启过程中的连接错误是预期的
503
+ print(f" ⏳ 服务正在重启中... ({elapsed:.1f}秒)")
504
+ except Exception:
505
+ # 其他错误
506
+ print(f" ⏳ 等待服务恢复... ({elapsed:.1f}秒)")
507
+
508
+ print(f"⏳ 等待中... ({elapsed:.1f}/{restart_max_wait}秒)")
509
+
510
+ print(f"❌ 重启等待超时 ({restart_max_wait}秒)")
511
+ result = f"重启命令已执行,但等待超时 ({restart_max_wait}秒)"
512
+ # 缓存结果
513
+ if use_cache:
514
+ self._command_cache[cache_key] = {
515
+ 'result': result,
516
+ 'timestamp': time.time()
517
+ }
518
+ return result
519
+ else:
520
+ # 正常命令执行
521
+ start_time = time.time()
522
+ result = self._call("tools/call", {
523
+ "name": "ssh_exec",
524
+ "arguments": {"name": server, "command": command, "timeout": timeout}
525
+ })
526
+ execution_time = time.time() - start_time
527
+
528
+ if "result" in result:
529
+ data = json.loads(result["result"]["content"][0]["text"])
530
+ output = data.get("stdout", "") or data.get("stderr", "") or data.get("error", "")
531
+ # 缓存结果
532
+ if use_cache:
533
+ self._command_cache[cache_key] = {
534
+ 'result': output,
535
+ 'timestamp': time.time()
536
+ }
537
+ return output
538
+
539
+ result_str = str(result)
540
+ # 缓存结果
541
+ if use_cache:
542
+ self._command_cache[cache_key] = {
543
+ 'result': result_str,
544
+ 'timestamp': time.time()
545
+ }
546
+ return result_str
547
+
548
+ async def execute_async(self, server: str, command: str, timeout: int = 60, use_cache: bool = True) -> str:
549
+ """异步执行命令,优化低带宽场景"""
550
+ # 使用asyncio的事件循环,优化异步性能
551
+ loop = asyncio.get_event_loop()
552
+ return await loop.run_in_executor(
553
+ None,
554
+ self.execute,
555
+ server,
556
+ command,
557
+ timeout,
558
+ False,
559
+ 2,
560
+ 60,
561
+ use_cache
562
+ )
563
+
564
+ def execute_batch(self, server: str, commands: List[str], timeout: int = 60,
565
+ use_cache: bool = True, parallel: bool = False) -> Dict[str, str]:
566
+ """批量执行命令,优化低带宽场景
567
+
568
+ Args:
569
+ server: 服务器名称
570
+ commands: 命令列表
571
+ timeout: 超时时间
572
+ use_cache: 是否使用缓存
573
+ parallel: 是否并行执行
574
+ """
575
+ results = {}
576
+
577
+ # 检查缓存
578
+ cached_commands = []
579
+ for command in commands:
580
+ cache_key = f"execute:{server}:{command}"
581
+ if use_cache and cache_key in self._command_cache:
582
+ cached = self._command_cache[cache_key]
583
+ if time.time() - cached['timestamp'] < self._cache_expiry:
584
+ results[command] = cached['result']
585
+ cached_commands.append(command)
586
+
587
+ # 执行未缓存的命令
588
+ uncached_commands = [cmd for cmd in commands if cmd not in cached_commands]
589
+
590
+ if parallel and uncached_commands:
591
+ # 并行执行
592
+ import concurrent.futures
593
+ with concurrent.futures.ThreadPoolExecutor(max_workers=min(5, len(uncached_commands))) as executor:
594
+ future_to_command = {
595
+ executor.submit(self.execute, server, cmd, timeout, False, 2, 60, use_cache): cmd
596
+ for cmd in uncached_commands
597
+ }
598
+ for future in concurrent.futures.as_completed(future_to_command):
599
+ cmd = future_to_command[future]
600
+ try:
601
+ results[cmd] = future.result()
602
+ except Exception as e:
603
+ results[cmd] = f"Error: {e}"
604
+ else:
605
+ # 串行执行
606
+ for command in uncached_commands:
607
+ try:
608
+ result = self.execute(server, command, timeout=timeout, use_cache=use_cache)
609
+ results[command] = result
610
+ except Exception as e:
611
+ results[command] = f"Error: {e}"
612
+
613
+ return results
614
+
615
+ async def execute_batch_async(self, server: str, commands: List[str], timeout: int = 60,
616
+ use_cache: bool = True) -> Dict[str, str]:
617
+ """异步批量执行命令,优化低带宽场景"""
618
+ # 优化:先检查缓存
619
+ cached_results = {}
620
+ uncached_commands = []
621
+
622
+ for command in commands:
623
+ cache_key = f"execute:{server}:{command}"
624
+ if use_cache and cache_key in self._command_cache:
625
+ cached = self._command_cache[cache_key]
626
+ if time.time() - cached['timestamp'] < self._cache_expiry:
627
+ cached_results[command] = cached['result']
628
+ continue
629
+ uncached_commands.append(command)
630
+
631
+ # 只对未缓存的命令执行异步操作
632
+ if not uncached_commands:
633
+ return cached_results
634
+
635
+ # 异步执行
636
+ tasks = []
637
+ for command in uncached_commands:
638
+ task = self.execute_async(server, command, timeout, use_cache)
639
+ tasks.append(task)
640
+
641
+ uncached_results = await asyncio.gather(*tasks)
642
+ uncached_dict = dict(zip(uncached_commands, uncached_results))
643
+
644
+ # 合并结果
645
+ cached_results.update(uncached_dict)
646
+ return cached_results
647
+
648
+ def list_connections(self) -> Dict[str, Any]:
649
+ """列出SSH连接"""
650
+ # 检查缓存
651
+ cache_key = "list_connections"
652
+ if cache_key in self._command_cache:
653
+ cached = self._command_cache[cache_key]
654
+ if time.time() - cached['timestamp'] < self._cache_expiry:
655
+ return cached['result']
656
+
657
+ result = self._call("tools/list")
658
+
659
+ # 缓存结果
660
+ self._command_cache[cache_key] = {
661
+ 'result': result,
662
+ 'timestamp': time.time()
663
+ }
664
+
665
+ return result
666
+
667
+ def connect(self, server: str) -> Dict[str, Any]:
668
+ """连接到SSH服务器"""
669
+ result = self._call("tools/call", {
670
+ "name": "ssh_connect",
671
+ "arguments": {"name": server}
672
+ })
673
+ return result
674
+
675
+ def disconnect(self, server: str) -> Dict[str, Any]:
676
+ """断开SSH连接"""
677
+ result = self._call("tools/call", {
678
+ "name": "ssh_disconnect",
679
+ "arguments": {"name": server}
680
+ })
681
+ return result
682
+
683
+ def start_monitoring(self, interval: int = 30, lightweight: bool = True):
684
+ """启动监控,优化低带宽场景
685
+
686
+ Args:
687
+ interval: 监控间隔(秒)
688
+ lightweight: 是否使用轻量级监控
689
+ """
690
+ def monitor():
691
+ while self._monitoring_enabled:
692
+ try:
693
+ if lightweight:
694
+ # 轻量级监控,减少网络请求
695
+ status = self.execute('local', 'uptime')
696
+ print(f"[监控] 服务器状态: {status.strip()}")
697
+ else:
698
+ # 完整监控
699
+ # 检查服务器状态
700
+ status = self.execute('local', 'uptime')
701
+ print(f"[监控] 服务器状态: {status.strip()}")
702
+
703
+ # 检查内存使用
704
+ memory = self.execute('local', 'free -h')
705
+ print(f"[监控] 内存使用:\n{memory.strip()}")
706
+
707
+ # 检查磁盘使用
708
+ disk = self.execute('local', 'df -h')
709
+ print(f"[监控] 磁盘使用:\n{disk.strip()}")
710
+
711
+ except Exception as e:
712
+ print(f"[监控] 错误: {e}")
713
+
714
+ time.sleep(interval)
715
+
716
+ self._monitoring_enabled = True
717
+ self._monitoring_thread = threading.Thread(target=monitor, daemon=True)
718
+ self._monitoring_thread.start()
719
+ print("✅ 监控已启动")
720
+
721
+ def stop_monitoring(self):
722
+ """停止监控"""
723
+ self._monitoring_enabled = False
724
+ if self._monitoring_thread:
725
+ self._monitoring_thread.join(timeout=5)
726
+ print("✅ 监控已停止")
727
+
728
+ def add_alert(self, condition: Callable[[], bool], message: str):
729
+ """添加告警"""
730
+ if condition():
731
+ self._alert_queue.put(message)
732
+ print(f"⚠️ 告警: {message}")
733
+
734
+ def get_alerts(self) -> List[str]:
735
+ """获取告警列表"""
736
+ alerts = []
737
+ while not self._alert_queue.empty():
738
+ alerts.append(self._alert_queue.get())
739
+ return alerts
740
+
741
+ def get_server_info(self) -> Dict[str, Any]:
742
+ """获取服务器信息"""
743
+ # 检查缓存
744
+ cache_key = "server_info"
745
+ if cache_key in self._command_cache:
746
+ cached = self._command_cache[cache_key]
747
+ if time.time() - cached['timestamp'] < self._cache_expiry * 5: # 服务器信息缓存时间更长
748
+ return cached['result']
749
+
750
+ result = self._call("initialize")
751
+
752
+ # 缓存结果
753
+ self._command_cache[cache_key] = {
754
+ 'result': result,
755
+ 'timestamp': time.time()
756
+ }
757
+
758
+ return result
759
+
760
+ def get_available_tools(self) -> Dict[str, Any]:
761
+ """获取可用工具"""
762
+ # 检查缓存
763
+ cache_key = "available_tools"
764
+ if cache_key in self._command_cache:
765
+ cached = self._command_cache[cache_key]
766
+ if time.time() - cached['timestamp'] < self._cache_expiry * 5: # 工具列表缓存时间更长
767
+ return cached['result']
768
+
769
+ result = self._call("tools/list")
770
+
771
+ # 缓存结果
772
+ self._command_cache[cache_key] = {
773
+ 'result': result,
774
+ 'timestamp': time.time()
775
+ }
776
+
777
+ return result
778
+
779
+
780
+
781
+ def shutdown(self):
782
+ """关闭资源"""
783
+ # 停止监控
784
+ self.stop_monitoring()
785
+
786
+ # 清理连接池
787
+ if self.enable_connection_pool:
788
+ with self._pool_lock:
789
+ while not self._connection_pool.empty():
790
+ try:
791
+ conn = self._connection_pool.get()
792
+ conn.close()
793
+ except:
794
+ pass
795
+
796
+ print("✅ Rcoder已关闭")
797
+
798
+
799
+ class RemoteHost:
800
+ """
801
+ 远程主机管理类
802
+ 提供类似本地主机的使用体验
803
+ """
804
+
805
+ def __init__(self, rcoder: RcoderCore, server: str = 'local'):
806
+ """
807
+ 初始化远程主机管理
808
+
809
+ Args:
810
+ rcoder: Rcoder核心实例
811
+ server: 服务器名称
812
+ """
813
+ self.rcoder = rcoder
814
+ self.server = server
815
+
816
+ def run(self, command: str, timeout: int = 60, wait_for_restart: bool = False,
817
+ use_cache: bool = True) -> str:
818
+ """运行命令,优化低带宽场景
819
+
820
+ Args:
821
+ command: 命令
822
+ timeout: 超时时间
823
+ wait_for_restart: 是否等待重启完成
824
+ use_cache: 是否使用缓存
825
+ """
826
+ return self.rcoder.execute(
827
+ self.server,
828
+ command,
829
+ timeout=timeout,
830
+ wait_for_restart=wait_for_restart,
831
+ use_cache=use_cache
832
+ )
833
+
834
+ async def run_async(self, command: str, timeout: int = 60, use_cache: bool = True) -> str:
835
+ """异步运行命令,优化低带宽场景"""
836
+ return await self.rcoder.execute_async(self.server, command, timeout, use_cache)
837
+
838
+ def run_batch(self, commands: List[str], timeout: int = 60,
839
+ use_cache: bool = True, parallel: bool = False) -> Dict[str, str]:
840
+ """批量运行命令,优化低带宽场景
841
+
842
+ Args:
843
+ commands: 命令列表
844
+ timeout: 超时时间
845
+ use_cache: 是否使用缓存
846
+ parallel: 是否并行执行
847
+ """
848
+ return self.rcoder.execute_batch(
849
+ self.server,
850
+ commands,
851
+ timeout=timeout,
852
+ use_cache=use_cache,
853
+ parallel=parallel
854
+ )
855
+
856
+ async def run_batch_async(self, commands: List[str], timeout: int = 60,
857
+ use_cache: bool = True) -> Dict[str, str]:
858
+ """异步批量运行命令,优化低带宽场景"""
859
+ return await self.rcoder.execute_batch_async(
860
+ self.server,
861
+ commands,
862
+ timeout=timeout,
863
+ use_cache=use_cache
864
+ )
865
+
866
+ def ls(self, path: str = '.', use_cache: bool = True) -> str:
867
+ """列出目录内容,优化低带宽场景"""
868
+ return self.run(f'ls -la {path}', use_cache=use_cache)
869
+
870
+ def cat(self, file: str, use_cache: bool = True) -> str:
871
+ """查看文件内容,优化低带宽场景"""
872
+ return self.run(f'cat {file}', use_cache=use_cache)
873
+
874
+ def mkdir(self, path: str) -> str:
875
+ """创建目录"""
876
+ return self.run(f'mkdir -p {path}', use_cache=False) # 不使用缓存,因为是修改操作
877
+
878
+ def rm(self, path: str, recursive: bool = False) -> str:
879
+ """删除文件或目录"""
880
+ cmd = f'rm -rf {path}' if recursive else f'rm {path}'
881
+ return self.run(cmd, use_cache=False) # 不使用缓存,因为是修改操作
882
+
883
+ def cp(self, source: str, destination: str) -> str:
884
+ """复制文件或目录"""
885
+ return self.run(f'cp -r {source} {destination}', use_cache=False) # 不使用缓存,因为是修改操作
886
+
887
+ def mv(self, source: str, destination: str) -> str:
888
+ """移动文件或目录"""
889
+ return self.run(f'mv {source} {destination}', use_cache=False) # 不使用缓存,因为是修改操作
890
+
891
+ def systemctl(self, action: str, service: str) -> str:
892
+ """管理系统服务"""
893
+ wait_for_restart = action == 'restart'
894
+ return self.run(
895
+ f'sudo systemctl {action} {service}',
896
+ wait_for_restart=wait_for_restart,
897
+ use_cache=False # 不使用缓存,因为是服务操作
898
+ )
899
+
900
+ def ps(self, use_cache: bool = True) -> str:
901
+ """查看进程,优化低带宽场景"""
902
+ return self.run('ps aux', use_cache=use_cache)
903
+
904
+ def top(self, use_cache: bool = True) -> str:
905
+ """查看系统负载,优化低带宽场景"""
906
+ return self.run('top -b -n 1', use_cache=use_cache)
907
+
908
+ def free(self, use_cache: bool = True) -> str:
909
+ """查看内存使用,优化低带宽场景"""
910
+ return self.run('free -h', use_cache=use_cache)
911
+
912
+ def df(self, use_cache: bool = True) -> str:
913
+ """查看磁盘使用,优化低带宽场景"""
914
+ return self.run('df -h', use_cache=use_cache)
915
+
916
+ def uptime(self, use_cache: bool = True) -> str:
917
+ """查看系统运行时间,优化低带宽场景"""
918
+ return self.run('uptime', use_cache=use_cache)
919
+
920
+ def hostname(self, use_cache: bool = True) -> str:
921
+ """查看主机名,优化低带宽场景"""
922
+ return self.run('hostname', use_cache=use_cache)
923
+
924
+ def ip(self, use_cache: bool = True) -> str:
925
+ """查看IP地址,优化低带宽场景"""
926
+ return self.run('ip addr', use_cache=use_cache)
927
+
928
+ def ping(self, host: str, count: int = 2) -> str:
929
+ """ping主机,优化低带宽场景(减少ping次数)"""
930
+ return self.run(f'ping -c {count} {host}', use_cache=False)
931
+
932
+
933
+ def get_remote_host(host: str = '192.168.1.8', port: int = 443, server: str = 'local',
934
+ use_https_disguise: bool = True, proxy_server: Optional[Tuple[str, int]] = None,
935
+ enable_compression: bool = True, enable_connection_pool: bool = True,
936
+ password: Optional[str] = None) -> RemoteHost:
937
+ """获取远程主机管理实例,优化低带宽场景
938
+
939
+ Args:
940
+ host: Rcoder服务器主机
941
+ port: Rcoder服务器端口 (默认443,支持HTTPS伪装)
942
+ server: 服务器名称
943
+ use_https_disguise: 是否使用HTTPS伪装
944
+ proxy_server: 中转服务器 (host, port),如 ('1.2.3.4', 443)
945
+ enable_compression: 是否启用数据压缩
946
+ enable_connection_pool: 是否启用连接池
947
+ password: 认证密码
948
+ """
949
+ rcoder = RcoderCore(
950
+ host=host,
951
+ port=port,
952
+ use_https_disguise=use_https_disguise,
953
+ proxy_server=proxy_server,
954
+ enable_compression=enable_compression,
955
+ enable_connection_pool=enable_connection_pool,
956
+ password=password
957
+ )
958
+ return RemoteHost(rcoder, server=server)