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.
- rcoder/__init__.py +8 -0
- rcoder/async_feedback.py +725 -0
- rcoder/async_proxy.py +556 -0
- rcoder/auto_optimizer.py +648 -0
- rcoder/cli.py +145 -0
- rcoder/conversational_config.py +345 -0
- rcoder/core.py +584 -0
- rcoder/core_optimized.py +958 -0
- rcoder/process_manager.py +443 -0
- rcoder/remote_tools.py +434 -0
- rcoder/server_installer.py +373 -0
- rcoder/utils.py +213 -0
- rcoder-1.0.0.dist-info/METADATA +442 -0
- rcoder-1.0.0.dist-info/RECORD +18 -0
- rcoder-1.0.0.dist-info/WHEEL +5 -0
- rcoder-1.0.0.dist-info/entry_points.txt +2 -0
- rcoder-1.0.0.dist-info/licenses/LICENSE +21 -0
- rcoder-1.0.0.dist-info/top_level.txt +1 -0
rcoder/core.py
ADDED
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Rcoder核心模块
|
|
4
|
+
实现远程代码执行与管理的核心功能
|
|
5
|
+
"""
|
|
6
|
+
import ssl
|
|
7
|
+
import socket
|
|
8
|
+
import json
|
|
9
|
+
import time
|
|
10
|
+
import asyncio
|
|
11
|
+
import hashlib
|
|
12
|
+
import threading
|
|
13
|
+
import queue
|
|
14
|
+
from typing import Dict, Any, Optional, List, Tuple, Callable
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class CommandResult:
|
|
19
|
+
"""命令执行结果"""
|
|
20
|
+
stdout: str
|
|
21
|
+
stderr: str
|
|
22
|
+
returncode: int
|
|
23
|
+
execution_time: float
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class BatchResult:
|
|
27
|
+
"""批量命令执行结果"""
|
|
28
|
+
command: str
|
|
29
|
+
results: Dict[str, CommandResult]
|
|
30
|
+
success_count: int
|
|
31
|
+
failure_count: int
|
|
32
|
+
total_time: float
|
|
33
|
+
|
|
34
|
+
class RcoderCore:
|
|
35
|
+
"""
|
|
36
|
+
Rcoder核心类
|
|
37
|
+
实现远程代码执行与管理的核心功能
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(self, host: str = '192.168.1.8', port: int = 443, use_https_disguise: bool = True, proxy_server: Optional[Tuple[str, int]] = None):
|
|
41
|
+
"""
|
|
42
|
+
初始化Rcoder核心
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
host: 服务器主机
|
|
46
|
+
port: 服务器端口 (默认443,支持HTTPS伪装)
|
|
47
|
+
use_https_disguise: 是否使用HTTPS伪装
|
|
48
|
+
proxy_server: 中转服务器 (host, port),如 ('1.2.3.4', 443)
|
|
49
|
+
"""
|
|
50
|
+
self.host = host
|
|
51
|
+
self.port = port
|
|
52
|
+
self.use_https_disguise = use_https_disguise
|
|
53
|
+
self.proxy_server = proxy_server
|
|
54
|
+
self.ssl_context = self._create_ssl_context()
|
|
55
|
+
self.token = None
|
|
56
|
+
self.public_key = None
|
|
57
|
+
self.private_key = None
|
|
58
|
+
self._monitoring_enabled = False
|
|
59
|
+
self._monitoring_thread = None
|
|
60
|
+
self._alert_queue = queue.Queue()
|
|
61
|
+
self._command_queue = queue.Queue()
|
|
62
|
+
self._results = {}
|
|
63
|
+
self._lock = threading.Lock()
|
|
64
|
+
self._session_id = hashlib.sha256(str(time.time()).encode()).hexdigest()
|
|
65
|
+
|
|
66
|
+
config_info = f"{host}:{port} (HTTPS伪装: {'启用' if use_https_disguise else '禁用'})"
|
|
67
|
+
if proxy_server:
|
|
68
|
+
config_info += f" (中转服务器: {proxy_server[0]}:{proxy_server[1]})"
|
|
69
|
+
|
|
70
|
+
print(f"✅ Rcoder初始化完成 (会话ID: {self._session_id[:8]})")
|
|
71
|
+
print(f"📡 连接配置: {config_info}")
|
|
72
|
+
|
|
73
|
+
def _create_ssl_context(self) -> ssl.SSLContext:
|
|
74
|
+
"""创建SSL上下文,增强HTTPS伪装"""
|
|
75
|
+
context = ssl.create_default_context()
|
|
76
|
+
|
|
77
|
+
# 增强HTTPS伪装
|
|
78
|
+
if self.use_https_disguise:
|
|
79
|
+
# 模拟标准HTTPS客户端行为
|
|
80
|
+
context.minimum_version = ssl.TLSVersion.TLSv1_2
|
|
81
|
+
context.maximum_version = ssl.TLSVersion.TLSv1_3
|
|
82
|
+
|
|
83
|
+
# 禁用主机名检查以支持自定义端口
|
|
84
|
+
context.check_hostname = False
|
|
85
|
+
context.verify_mode = ssl.CERT_NONE
|
|
86
|
+
|
|
87
|
+
# 模拟常见浏览器的密码套件偏好
|
|
88
|
+
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')
|
|
89
|
+
else:
|
|
90
|
+
# 标准模式
|
|
91
|
+
context.check_hostname = False
|
|
92
|
+
context.verify_mode = ssl.CERT_NONE
|
|
93
|
+
|
|
94
|
+
return context
|
|
95
|
+
|
|
96
|
+
def _call(self, method: str, params: Dict[str, Any] = None,
|
|
97
|
+
retry_on_failure: bool = True, max_retries: int = 5) -> Dict[str, Any]:
|
|
98
|
+
"""执行JSON-RPC调用,增强HTTPS伪装
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
method: RPC方法名
|
|
102
|
+
params: RPC参数
|
|
103
|
+
retry_on_failure: 是否在失败时重试
|
|
104
|
+
max_retries: 最大重试次数
|
|
105
|
+
"""
|
|
106
|
+
retry_count = 0
|
|
107
|
+
|
|
108
|
+
while retry_count <= max_retries:
|
|
109
|
+
try:
|
|
110
|
+
# 建立TCP连接(支持中转服务器)
|
|
111
|
+
if self.proxy_server:
|
|
112
|
+
print(f" 🔄 通过中转服务器连接: {self.proxy_server[0]}:{self.proxy_server[1]}")
|
|
113
|
+
sock = socket.create_connection((self.proxy_server[0], self.proxy_server[1]), timeout=30)
|
|
114
|
+
|
|
115
|
+
# 发送代理连接请求
|
|
116
|
+
proxy_connect = f"CONNECT {self.host}:{self.port} HTTP/1.1\r\n"
|
|
117
|
+
proxy_connect += f"Host: {self.host}:{self.port}\r\n"
|
|
118
|
+
proxy_connect += "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36\r\n"
|
|
119
|
+
proxy_connect += "Connection: Keep-Alive\r\n"
|
|
120
|
+
proxy_connect += "\r\n"
|
|
121
|
+
|
|
122
|
+
sock.send(proxy_connect.encode())
|
|
123
|
+
# 读取代理响应
|
|
124
|
+
proxy_response = sock.recv(4096)
|
|
125
|
+
if b"200 Connection established" not in proxy_response:
|
|
126
|
+
raise Exception(f"Proxy connection failed: {proxy_response.decode()}")
|
|
127
|
+
print(f" ✅ 中转服务器连接成功")
|
|
128
|
+
else:
|
|
129
|
+
# 直接连接
|
|
130
|
+
sock = socket.create_connection((self.host, self.port), timeout=30)
|
|
131
|
+
|
|
132
|
+
# 增强HTTPS伪装:添加HTTP请求头模拟
|
|
133
|
+
if self.use_https_disguise:
|
|
134
|
+
# 模拟HTTP请求头
|
|
135
|
+
http_headers = {
|
|
136
|
+
"Host": f"{self.host}:{self.port}",
|
|
137
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
138
|
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
|
|
139
|
+
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
|
|
140
|
+
"Accept-Encoding": "gzip, deflate, br",
|
|
141
|
+
"Connection": "keep-alive",
|
|
142
|
+
"Upgrade-Insecure-Requests": "1",
|
|
143
|
+
"Sec-Fetch-Dest": "document",
|
|
144
|
+
"Sec-Fetch-Mode": "navigate",
|
|
145
|
+
"Sec-Fetch-Site": "none",
|
|
146
|
+
"Sec-Fetch-User": "?1",
|
|
147
|
+
"Cache-Control": "max-age=0"
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
# 构建HTTP请求
|
|
151
|
+
http_request = "GET / HTTP/1.1\r\n"
|
|
152
|
+
for key, value in http_headers.items():
|
|
153
|
+
http_request += f"{key}: {value}\r\n"
|
|
154
|
+
http_request += "\r\n"
|
|
155
|
+
|
|
156
|
+
# 发送HTTP请求作为伪装
|
|
157
|
+
sock.send(http_request.encode())
|
|
158
|
+
# 读取HTTP响应(丢弃)
|
|
159
|
+
sock.recv(4096)
|
|
160
|
+
|
|
161
|
+
# 包装为TLS连接
|
|
162
|
+
server_hostname = f"{self.host}" if self.use_https_disguise else "rcoder"
|
|
163
|
+
tls_sock = self.ssl_context.wrap_socket(sock, server_hostname=server_hostname)
|
|
164
|
+
|
|
165
|
+
# 构建请求,增强安全性
|
|
166
|
+
request = {
|
|
167
|
+
"jsonrpc": "2.0",
|
|
168
|
+
"id": int(time.time() * 1000),
|
|
169
|
+
"method": method,
|
|
170
|
+
"params": params or {},
|
|
171
|
+
"session_id": self._session_id[:16],
|
|
172
|
+
"timestamp": int(time.time()),
|
|
173
|
+
"version": "1.0"
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
# 发送请求
|
|
177
|
+
request_data = json.dumps(request).encode() + b"\n"
|
|
178
|
+
tls_sock.send(request_data)
|
|
179
|
+
|
|
180
|
+
# 接收响应
|
|
181
|
+
response = tls_sock.recv(65536)
|
|
182
|
+
tls_sock.close()
|
|
183
|
+
|
|
184
|
+
if not response:
|
|
185
|
+
if retry_on_failure and retry_count < max_retries:
|
|
186
|
+
retry_count += 1
|
|
187
|
+
time.sleep(1)
|
|
188
|
+
continue
|
|
189
|
+
raise Exception("Empty response from server")
|
|
190
|
+
|
|
191
|
+
# 解析响应
|
|
192
|
+
return json.loads(response)
|
|
193
|
+
|
|
194
|
+
except (socket.error, ConnectionResetError, ConnectionRefusedError) as e:
|
|
195
|
+
if retry_on_failure and retry_count < max_retries:
|
|
196
|
+
retry_count += 1
|
|
197
|
+
time.sleep(1)
|
|
198
|
+
continue
|
|
199
|
+
raise
|
|
200
|
+
except json.JSONDecodeError as e:
|
|
201
|
+
if retry_on_failure and retry_count < max_retries:
|
|
202
|
+
retry_count += 1
|
|
203
|
+
time.sleep(1)
|
|
204
|
+
continue
|
|
205
|
+
raise
|
|
206
|
+
except Exception as e:
|
|
207
|
+
if retry_on_failure and retry_count < max_retries:
|
|
208
|
+
retry_count += 1
|
|
209
|
+
time.sleep(1)
|
|
210
|
+
continue
|
|
211
|
+
raise
|
|
212
|
+
|
|
213
|
+
def execute(self, server: str, command: str, timeout: int = 60,
|
|
214
|
+
wait_for_restart: bool = False, restart_check_interval: int = 2,
|
|
215
|
+
restart_max_wait: int = 60) -> str:
|
|
216
|
+
"""执行命令
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
server: 服务器名称
|
|
220
|
+
command: 命令
|
|
221
|
+
timeout: 超时时间
|
|
222
|
+
wait_for_restart: 是否等待重启完成
|
|
223
|
+
restart_check_interval: 重启检查间隔
|
|
224
|
+
restart_max_wait: 最大重启等待时间
|
|
225
|
+
"""
|
|
226
|
+
# 检查是否为重启命令
|
|
227
|
+
is_restart_command = any(keyword in command.lower() for keyword in [
|
|
228
|
+
'restart', 'systemctl restart', 'service restart', 'reboot'
|
|
229
|
+
])
|
|
230
|
+
|
|
231
|
+
if is_restart_command and wait_for_restart:
|
|
232
|
+
print(f"执行重启命令并等待完成...")
|
|
233
|
+
print(f"命令: {command}")
|
|
234
|
+
print(f"最大等待时间: {restart_max_wait}秒")
|
|
235
|
+
|
|
236
|
+
try:
|
|
237
|
+
# 执行重启命令
|
|
238
|
+
result = self._call("tools/call", {
|
|
239
|
+
"name": "ssh_exec",
|
|
240
|
+
"arguments": {"name": server, "command": command, "timeout": timeout}
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
if "result" in result:
|
|
244
|
+
data = json.loads(result["result"]["content"][0]["text"])
|
|
245
|
+
print(f"重启命令执行结果: {data.get('stdout', '').strip() or data.get('stderr', '').strip()}")
|
|
246
|
+
except Exception as e:
|
|
247
|
+
print(f"重启命令执行异常: {e}")
|
|
248
|
+
# 继续等待,因为重启命令可能已经开始执行
|
|
249
|
+
|
|
250
|
+
# 等待重启完成
|
|
251
|
+
start_time = time.time()
|
|
252
|
+
elapsed = 0
|
|
253
|
+
|
|
254
|
+
print("等待服务重启...")
|
|
255
|
+
while elapsed < restart_max_wait:
|
|
256
|
+
time.sleep(restart_check_interval)
|
|
257
|
+
elapsed = time.time() - start_time
|
|
258
|
+
|
|
259
|
+
try:
|
|
260
|
+
# 尝试重新连接并检查服务是否可用
|
|
261
|
+
test_result = self._call(
|
|
262
|
+
"tools/call",
|
|
263
|
+
{
|
|
264
|
+
"name": "ssh_exec",
|
|
265
|
+
"arguments": {"name": server, "command": "echo 'Rcoder service available'", "timeout": 10}
|
|
266
|
+
},
|
|
267
|
+
retry_on_failure=True,
|
|
268
|
+
max_retries=2
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
if "result" in test_result:
|
|
272
|
+
test_data = json.loads(test_result["result"]["content"][0]["text"])
|
|
273
|
+
if "Rcoder service available" in test_data.get("stdout", ""):
|
|
274
|
+
print(f"✅ 服务已重启完成 (耗时: {elapsed:.1f}秒)")
|
|
275
|
+
return f"重启完成 (耗时: {elapsed:.1f}秒)"
|
|
276
|
+
|
|
277
|
+
except (socket.error, ConnectionResetError, ConnectionRefusedError) as e:
|
|
278
|
+
# 重启过程中的连接错误是预期的
|
|
279
|
+
print(f" ⏳ 服务正在重启中... ({elapsed:.1f}秒)")
|
|
280
|
+
except json.JSONDecodeError as e:
|
|
281
|
+
# 重启过程中可能会出现响应不完整的情况
|
|
282
|
+
print(f" ⏳ 服务响应不完整,继续等待... ({elapsed:.1f}秒)")
|
|
283
|
+
except Exception as e:
|
|
284
|
+
# 其他错误
|
|
285
|
+
print(f" ⏳ 等待服务恢复... ({elapsed:.1f}秒)")
|
|
286
|
+
|
|
287
|
+
print(f"⏳ 等待中... ({elapsed:.1f}/{restart_max_wait}秒)")
|
|
288
|
+
|
|
289
|
+
print(f"❌ 重启等待超时 ({restart_max_wait}秒)")
|
|
290
|
+
return f"重启命令已执行,但等待超时 ({restart_max_wait}秒)"
|
|
291
|
+
else:
|
|
292
|
+
# 正常命令执行
|
|
293
|
+
start_time = time.time()
|
|
294
|
+
result = self._call("tools/call", {
|
|
295
|
+
"name": "ssh_exec",
|
|
296
|
+
"arguments": {"name": server, "command": command, "timeout": timeout}
|
|
297
|
+
})
|
|
298
|
+
execution_time = time.time() - start_time
|
|
299
|
+
|
|
300
|
+
if "result" in result:
|
|
301
|
+
data = json.loads(result["result"]["content"][0]["text"])
|
|
302
|
+
return data.get("stdout", "") or data.get("stderr", "") or data.get("error", "")
|
|
303
|
+
return str(result)
|
|
304
|
+
|
|
305
|
+
async def execute_async(self, server: str, command: str, timeout: int = 60) -> str:
|
|
306
|
+
"""异步执行命令"""
|
|
307
|
+
loop = asyncio.get_event_loop()
|
|
308
|
+
return await loop.run_in_executor(
|
|
309
|
+
None,
|
|
310
|
+
self.execute,
|
|
311
|
+
server,
|
|
312
|
+
command,
|
|
313
|
+
timeout
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
def execute_batch(self, server: str, commands: List[str], timeout: int = 60) -> Dict[str, str]:
|
|
317
|
+
"""批量执行命令
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
server: 服务器名称
|
|
321
|
+
commands: 命令列表
|
|
322
|
+
timeout: 超时时间
|
|
323
|
+
"""
|
|
324
|
+
results = {}
|
|
325
|
+
for command in commands:
|
|
326
|
+
try:
|
|
327
|
+
result = self.execute(server, command, timeout=timeout)
|
|
328
|
+
results[command] = result
|
|
329
|
+
except Exception as e:
|
|
330
|
+
results[command] = f"Error: {e}"
|
|
331
|
+
return results
|
|
332
|
+
|
|
333
|
+
async def execute_batch_async(self, server: str, commands: List[str], timeout: int = 60) -> Dict[str, str]:
|
|
334
|
+
"""异步批量执行命令"""
|
|
335
|
+
tasks = []
|
|
336
|
+
for command in commands:
|
|
337
|
+
task = self.execute_async(server, command, timeout)
|
|
338
|
+
tasks.append(task)
|
|
339
|
+
|
|
340
|
+
results = await asyncio.gather(*tasks)
|
|
341
|
+
return dict(zip(commands, results))
|
|
342
|
+
|
|
343
|
+
def list_connections(self) -> Dict[str, Any]:
|
|
344
|
+
"""列出SSH连接"""
|
|
345
|
+
result = self._call("tools/call", {"name": "ssh_list", "arguments": {}})
|
|
346
|
+
if "result" in result:
|
|
347
|
+
return json.loads(result["result"]["content"][0]["text"])
|
|
348
|
+
return result
|
|
349
|
+
|
|
350
|
+
def connect(self, server: str) -> Dict[str, Any]:
|
|
351
|
+
"""连接到SSH服务器"""
|
|
352
|
+
result = self._call("tools/call", {
|
|
353
|
+
"name": "ssh_connect",
|
|
354
|
+
"arguments": {"name": server}
|
|
355
|
+
})
|
|
356
|
+
if "result" in result:
|
|
357
|
+
return json.loads(result["result"]["content"][0]["text"])
|
|
358
|
+
return result
|
|
359
|
+
|
|
360
|
+
def disconnect(self, server: str) -> Dict[str, Any]:
|
|
361
|
+
"""断开SSH连接"""
|
|
362
|
+
result = self._call("tools/call", {
|
|
363
|
+
"name": "ssh_disconnect",
|
|
364
|
+
"arguments": {"name": server}
|
|
365
|
+
})
|
|
366
|
+
if "result" in result:
|
|
367
|
+
return json.loads(result["result"]["content"][0]["text"])
|
|
368
|
+
return result
|
|
369
|
+
|
|
370
|
+
def start_monitoring(self, interval: int = 30):
|
|
371
|
+
"""启动监控
|
|
372
|
+
|
|
373
|
+
Args:
|
|
374
|
+
interval: 监控间隔(秒)
|
|
375
|
+
"""
|
|
376
|
+
def monitor():
|
|
377
|
+
while self._monitoring_enabled:
|
|
378
|
+
try:
|
|
379
|
+
# 检查服务器状态
|
|
380
|
+
status = self.execute('local', 'uptime')
|
|
381
|
+
print(f"[监控] 服务器状态: {status.strip()}")
|
|
382
|
+
|
|
383
|
+
# 检查内存使用
|
|
384
|
+
memory = self.execute('local', 'free -h')
|
|
385
|
+
print(f"[监控] 内存使用:\n{memory.strip()}")
|
|
386
|
+
|
|
387
|
+
# 检查磁盘使用
|
|
388
|
+
disk = self.execute('local', 'df -h')
|
|
389
|
+
print(f"[监控] 磁盘使用:\n{disk.strip()}")
|
|
390
|
+
|
|
391
|
+
except Exception as e:
|
|
392
|
+
print(f"[监控] 错误: {e}")
|
|
393
|
+
|
|
394
|
+
time.sleep(interval)
|
|
395
|
+
|
|
396
|
+
self._monitoring_enabled = True
|
|
397
|
+
self._monitoring_thread = threading.Thread(target=monitor, daemon=True)
|
|
398
|
+
self._monitoring_thread.start()
|
|
399
|
+
print("✅ 监控已启动")
|
|
400
|
+
|
|
401
|
+
def stop_monitoring(self):
|
|
402
|
+
"""停止监控"""
|
|
403
|
+
self._monitoring_enabled = False
|
|
404
|
+
if self._monitoring_thread:
|
|
405
|
+
self._monitoring_thread.join(timeout=5)
|
|
406
|
+
print("✅ 监控已停止")
|
|
407
|
+
|
|
408
|
+
def add_alert(self, condition: Callable[[], bool], message: str):
|
|
409
|
+
"""添加告警
|
|
410
|
+
|
|
411
|
+
Args:
|
|
412
|
+
condition: 告警条件
|
|
413
|
+
message: 告警消息
|
|
414
|
+
"""
|
|
415
|
+
if condition():
|
|
416
|
+
self._alert_queue.put(message)
|
|
417
|
+
print(f"⚠️ 告警: {message}")
|
|
418
|
+
|
|
419
|
+
def get_alerts(self) -> List[str]:
|
|
420
|
+
"""获取告警列表"""
|
|
421
|
+
alerts = []
|
|
422
|
+
while not self._alert_queue.empty():
|
|
423
|
+
alerts.append(self._alert_queue.get())
|
|
424
|
+
return alerts
|
|
425
|
+
|
|
426
|
+
def get_server_info(self) -> Dict[str, Any]:
|
|
427
|
+
"""获取服务器信息"""
|
|
428
|
+
result = self._call("initialize")
|
|
429
|
+
if "result" in result:
|
|
430
|
+
return result["result"]
|
|
431
|
+
return result
|
|
432
|
+
|
|
433
|
+
def get_available_tools(self) -> Dict[str, Any]:
|
|
434
|
+
"""获取可用工具"""
|
|
435
|
+
result = self._call("tools/list")
|
|
436
|
+
if "result" in result:
|
|
437
|
+
return result["result"]
|
|
438
|
+
return result
|
|
439
|
+
|
|
440
|
+
def setup_key_auth(self, public_key: str, private_key: str):
|
|
441
|
+
"""设置密钥认证
|
|
442
|
+
|
|
443
|
+
Args:
|
|
444
|
+
public_key: 公钥
|
|
445
|
+
private_key: 私钥
|
|
446
|
+
"""
|
|
447
|
+
self.public_key = public_key
|
|
448
|
+
self.private_key = private_key
|
|
449
|
+
print("✅ 密钥认证已设置")
|
|
450
|
+
|
|
451
|
+
def shutdown(self):
|
|
452
|
+
"""关闭资源"""
|
|
453
|
+
self.stop_monitoring()
|
|
454
|
+
print("✅ Rcoder已关闭")
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
class RemoteHost:
|
|
458
|
+
"""
|
|
459
|
+
远程主机管理类
|
|
460
|
+
提供类似本地主机的使用体验
|
|
461
|
+
"""
|
|
462
|
+
|
|
463
|
+
def __init__(self, rcoder: RcoderCore, server: str = 'local'):
|
|
464
|
+
"""
|
|
465
|
+
初始化远程主机管理
|
|
466
|
+
|
|
467
|
+
Args:
|
|
468
|
+
rcoder: Rcoder核心实例
|
|
469
|
+
server: 服务器名称
|
|
470
|
+
"""
|
|
471
|
+
self.rcoder = rcoder
|
|
472
|
+
self.server = server
|
|
473
|
+
|
|
474
|
+
def run(self, command: str, timeout: int = 60, wait_for_restart: bool = False) -> str:
|
|
475
|
+
"""运行命令
|
|
476
|
+
|
|
477
|
+
Args:
|
|
478
|
+
command: 命令
|
|
479
|
+
timeout: 超时时间
|
|
480
|
+
wait_for_restart: 是否等待重启完成
|
|
481
|
+
"""
|
|
482
|
+
return self.rcoder.execute(
|
|
483
|
+
self.server,
|
|
484
|
+
command,
|
|
485
|
+
timeout=timeout,
|
|
486
|
+
wait_for_restart=wait_for_restart
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
async def run_async(self, command: str, timeout: int = 60) -> str:
|
|
490
|
+
"""异步运行命令"""
|
|
491
|
+
return await self.rcoder.execute_async(self.server, command, timeout)
|
|
492
|
+
|
|
493
|
+
def run_batch(self, commands: List[str], timeout: int = 60) -> Dict[str, str]:
|
|
494
|
+
"""批量运行命令"""
|
|
495
|
+
return self.rcoder.execute_batch(self.server, commands, timeout)
|
|
496
|
+
|
|
497
|
+
async def run_batch_async(self, commands: List[str], timeout: int = 60) -> Dict[str, str]:
|
|
498
|
+
"""异步批量运行命令"""
|
|
499
|
+
return await self.rcoder.execute_batch_async(self.server, commands, timeout)
|
|
500
|
+
|
|
501
|
+
def ls(self, path: str = '.') -> str:
|
|
502
|
+
"""列出目录内容"""
|
|
503
|
+
return self.run(f'ls -la {path}')
|
|
504
|
+
|
|
505
|
+
def cat(self, file: str) -> str:
|
|
506
|
+
"""查看文件内容"""
|
|
507
|
+
return self.run(f'cat {file}')
|
|
508
|
+
|
|
509
|
+
def mkdir(self, path: str) -> str:
|
|
510
|
+
"""创建目录"""
|
|
511
|
+
return self.run(f'mkdir -p {path}')
|
|
512
|
+
|
|
513
|
+
def rm(self, path: str, recursive: bool = False) -> str:
|
|
514
|
+
"""删除文件或目录"""
|
|
515
|
+
if recursive:
|
|
516
|
+
return self.run(f'rm -rf {path}')
|
|
517
|
+
return self.run(f'rm {path}')
|
|
518
|
+
|
|
519
|
+
def cp(self, source: str, destination: str) -> str:
|
|
520
|
+
"""复制文件或目录"""
|
|
521
|
+
return self.run(f'cp -r {source} {destination}')
|
|
522
|
+
|
|
523
|
+
def mv(self, source: str, destination: str) -> str:
|
|
524
|
+
"""移动文件或目录"""
|
|
525
|
+
return self.run(f'mv {source} {destination}')
|
|
526
|
+
|
|
527
|
+
def systemctl(self, action: str, service: str) -> str:
|
|
528
|
+
"""管理系统服务
|
|
529
|
+
|
|
530
|
+
Args:
|
|
531
|
+
action: 动作 (start, stop, restart, status)
|
|
532
|
+
service: 服务名称
|
|
533
|
+
"""
|
|
534
|
+
wait_for_restart = action == 'restart'
|
|
535
|
+
return self.run(
|
|
536
|
+
f'sudo systemctl {action} {service}',
|
|
537
|
+
wait_for_restart=wait_for_restart
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
def ps(self) -> str:
|
|
541
|
+
"""查看进程"""
|
|
542
|
+
return self.run('ps aux')
|
|
543
|
+
|
|
544
|
+
def top(self) -> str:
|
|
545
|
+
"""查看系统负载"""
|
|
546
|
+
return self.run('top -b -n 1')
|
|
547
|
+
|
|
548
|
+
def free(self) -> str:
|
|
549
|
+
"""查看内存使用"""
|
|
550
|
+
return self.run('free -h')
|
|
551
|
+
|
|
552
|
+
def df(self) -> str:
|
|
553
|
+
"""查看磁盘使用"""
|
|
554
|
+
return self.run('df -h')
|
|
555
|
+
|
|
556
|
+
def uptime(self) -> str:
|
|
557
|
+
"""查看系统运行时间"""
|
|
558
|
+
return self.run('uptime')
|
|
559
|
+
|
|
560
|
+
def hostname(self) -> str:
|
|
561
|
+
"""查看主机名"""
|
|
562
|
+
return self.run('hostname')
|
|
563
|
+
|
|
564
|
+
def ip(self) -> str:
|
|
565
|
+
"""查看IP地址"""
|
|
566
|
+
return self.run('ip addr')
|
|
567
|
+
|
|
568
|
+
def ping(self, host: str, count: int = 4) -> str:
|
|
569
|
+
"""ping主机"""
|
|
570
|
+
return self.run(f'ping -c {count} {host}')
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
def get_remote_host(host: str = '192.168.1.8', port: int = 443, server: str = 'local', use_https_disguise: bool = True, proxy_server: Optional[Tuple[str, int]] = None) -> RemoteHost:
|
|
574
|
+
"""获取远程主机管理实例
|
|
575
|
+
|
|
576
|
+
Args:
|
|
577
|
+
host: Rcoder服务器主机
|
|
578
|
+
port: Rcoder服务器端口 (默认443,支持HTTPS伪装)
|
|
579
|
+
server: 服务器名称
|
|
580
|
+
use_https_disguise: 是否使用HTTPS伪装
|
|
581
|
+
proxy_server: 中转服务器 (host, port),如 ('1.2.3.4', 443)
|
|
582
|
+
"""
|
|
583
|
+
rcoder = RcoderCore(host=host, port=port, use_https_disguise=use_https_disguise, proxy_server=proxy_server)
|
|
584
|
+
return RemoteHost(rcoder, server=server)
|