jarvis-ai-assistant 0.1.222__py3-none-any.whl → 0.7.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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +1143 -245
- jarvis/jarvis_agent/agent_manager.py +97 -0
- jarvis/jarvis_agent/builtin_input_handler.py +12 -10
- jarvis/jarvis_agent/config_editor.py +57 -0
- jarvis/jarvis_agent/edit_file_handler.py +392 -99
- jarvis/jarvis_agent/event_bus.py +48 -0
- jarvis/jarvis_agent/events.py +157 -0
- jarvis/jarvis_agent/file_context_handler.py +79 -0
- jarvis/jarvis_agent/file_methodology_manager.py +117 -0
- jarvis/jarvis_agent/jarvis.py +1117 -147
- jarvis/jarvis_agent/main.py +78 -34
- jarvis/jarvis_agent/memory_manager.py +195 -0
- jarvis/jarvis_agent/methodology_share_manager.py +174 -0
- jarvis/jarvis_agent/prompt_manager.py +82 -0
- jarvis/jarvis_agent/prompts.py +46 -9
- jarvis/jarvis_agent/protocols.py +4 -1
- jarvis/jarvis_agent/rewrite_file_handler.py +141 -0
- jarvis/jarvis_agent/run_loop.py +146 -0
- jarvis/jarvis_agent/session_manager.py +9 -9
- jarvis/jarvis_agent/share_manager.py +228 -0
- jarvis/jarvis_agent/shell_input_handler.py +23 -3
- jarvis/jarvis_agent/stdio_redirect.py +295 -0
- jarvis/jarvis_agent/task_analyzer.py +212 -0
- jarvis/jarvis_agent/task_manager.py +154 -0
- jarvis/jarvis_agent/task_planner.py +496 -0
- jarvis/jarvis_agent/tool_executor.py +8 -4
- jarvis/jarvis_agent/tool_share_manager.py +139 -0
- jarvis/jarvis_agent/user_interaction.py +42 -0
- jarvis/jarvis_agent/utils.py +54 -0
- jarvis/jarvis_agent/web_bridge.py +189 -0
- jarvis/jarvis_agent/web_output_sink.py +53 -0
- jarvis/jarvis_agent/web_server.py +751 -0
- jarvis/jarvis_c2rust/__init__.py +26 -0
- jarvis/jarvis_c2rust/cli.py +613 -0
- jarvis/jarvis_c2rust/collector.py +258 -0
- jarvis/jarvis_c2rust/library_replacer.py +1122 -0
- jarvis/jarvis_c2rust/llm_module_agent.py +1300 -0
- jarvis/jarvis_c2rust/optimizer.py +960 -0
- jarvis/jarvis_c2rust/scanner.py +1681 -0
- jarvis/jarvis_c2rust/transpiler.py +2325 -0
- jarvis/jarvis_code_agent/build_validation_config.py +133 -0
- jarvis/jarvis_code_agent/code_agent.py +1605 -178
- jarvis/jarvis_code_agent/code_analyzer/__init__.py +62 -0
- jarvis/jarvis_code_agent/code_analyzer/base_language.py +74 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +44 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +102 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +59 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +125 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +69 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +38 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +44 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +38 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +50 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +93 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +129 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +54 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +154 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator.py +43 -0
- jarvis/jarvis_code_agent/code_analyzer/context_manager.py +363 -0
- jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +18 -0
- jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +132 -0
- jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +330 -0
- jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +781 -0
- jarvis/jarvis_code_agent/code_analyzer/language_registry.py +185 -0
- jarvis/jarvis_code_agent/code_analyzer/language_support.py +89 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +31 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +231 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +183 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +219 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +209 -0
- jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +451 -0
- jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +77 -0
- jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +48 -0
- jarvis/jarvis_code_agent/lint.py +275 -13
- jarvis/jarvis_code_agent/utils.py +142 -0
- jarvis/jarvis_code_analysis/checklists/loader.py +20 -6
- jarvis/jarvis_code_analysis/code_review.py +583 -548
- jarvis/jarvis_data/config_schema.json +339 -28
- jarvis/jarvis_git_squash/main.py +22 -13
- jarvis/jarvis_git_utils/git_commiter.py +171 -55
- jarvis/jarvis_mcp/sse_mcp_client.py +22 -15
- jarvis/jarvis_mcp/stdio_mcp_client.py +4 -4
- jarvis/jarvis_mcp/streamable_mcp_client.py +36 -16
- jarvis/jarvis_memory_organizer/memory_organizer.py +753 -0
- jarvis/jarvis_methodology/main.py +48 -63
- jarvis/jarvis_multi_agent/__init__.py +302 -43
- jarvis/jarvis_multi_agent/main.py +70 -24
- jarvis/jarvis_platform/ai8.py +40 -23
- jarvis/jarvis_platform/base.py +210 -49
- jarvis/jarvis_platform/human.py +11 -1
- jarvis/jarvis_platform/kimi.py +82 -76
- jarvis/jarvis_platform/openai.py +73 -1
- jarvis/jarvis_platform/registry.py +8 -15
- jarvis/jarvis_platform/tongyi.py +115 -101
- jarvis/jarvis_platform/yuanbao.py +89 -63
- jarvis/jarvis_platform_manager/main.py +194 -132
- jarvis/jarvis_platform_manager/service.py +122 -86
- jarvis/jarvis_rag/cli.py +156 -53
- jarvis/jarvis_rag/embedding_manager.py +155 -12
- jarvis/jarvis_rag/llm_interface.py +10 -13
- jarvis/jarvis_rag/query_rewriter.py +63 -12
- jarvis/jarvis_rag/rag_pipeline.py +222 -40
- jarvis/jarvis_rag/reranker.py +26 -3
- jarvis/jarvis_rag/retriever.py +270 -14
- jarvis/jarvis_sec/__init__.py +3605 -0
- jarvis/jarvis_sec/checkers/__init__.py +32 -0
- jarvis/jarvis_sec/checkers/c_checker.py +2680 -0
- jarvis/jarvis_sec/checkers/rust_checker.py +1108 -0
- jarvis/jarvis_sec/cli.py +116 -0
- jarvis/jarvis_sec/report.py +257 -0
- jarvis/jarvis_sec/status.py +264 -0
- jarvis/jarvis_sec/types.py +20 -0
- jarvis/jarvis_sec/workflow.py +219 -0
- jarvis/jarvis_smart_shell/main.py +405 -137
- jarvis/jarvis_stats/__init__.py +13 -0
- jarvis/jarvis_stats/cli.py +387 -0
- jarvis/jarvis_stats/stats.py +711 -0
- jarvis/jarvis_stats/storage.py +612 -0
- jarvis/jarvis_stats/visualizer.py +282 -0
- jarvis/jarvis_tools/ask_user.py +1 -0
- jarvis/jarvis_tools/base.py +18 -2
- jarvis/jarvis_tools/clear_memory.py +239 -0
- jarvis/jarvis_tools/cli/main.py +220 -144
- jarvis/jarvis_tools/execute_script.py +52 -12
- jarvis/jarvis_tools/file_analyzer.py +17 -12
- jarvis/jarvis_tools/generate_new_tool.py +46 -24
- jarvis/jarvis_tools/read_code.py +277 -18
- jarvis/jarvis_tools/read_symbols.py +141 -0
- jarvis/jarvis_tools/read_webpage.py +86 -13
- jarvis/jarvis_tools/registry.py +294 -90
- jarvis/jarvis_tools/retrieve_memory.py +227 -0
- jarvis/jarvis_tools/save_memory.py +194 -0
- jarvis/jarvis_tools/search_web.py +62 -28
- jarvis/jarvis_tools/sub_agent.py +205 -0
- jarvis/jarvis_tools/sub_code_agent.py +217 -0
- jarvis/jarvis_tools/virtual_tty.py +330 -62
- jarvis/jarvis_utils/builtin_replace_map.py +4 -5
- jarvis/jarvis_utils/clipboard.py +90 -0
- jarvis/jarvis_utils/config.py +607 -50
- jarvis/jarvis_utils/embedding.py +3 -0
- jarvis/jarvis_utils/fzf.py +57 -0
- jarvis/jarvis_utils/git_utils.py +251 -29
- jarvis/jarvis_utils/globals.py +174 -17
- jarvis/jarvis_utils/http.py +58 -79
- jarvis/jarvis_utils/input.py +899 -153
- jarvis/jarvis_utils/methodology.py +210 -83
- jarvis/jarvis_utils/output.py +220 -137
- jarvis/jarvis_utils/utils.py +1906 -135
- jarvis_ai_assistant-0.7.0.dist-info/METADATA +465 -0
- jarvis_ai_assistant-0.7.0.dist-info/RECORD +192 -0
- {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/entry_points.txt +8 -2
- jarvis/jarvis_git_details/main.py +0 -265
- jarvis/jarvis_platform/oyi.py +0 -357
- jarvis/jarvis_tools/edit_file.py +0 -255
- jarvis/jarvis_tools/rewrite_file.py +0 -195
- jarvis_ai_assistant-0.1.222.dist-info/METADATA +0 -767
- jarvis_ai_assistant-0.1.222.dist-info/RECORD +0 -110
- /jarvis/{jarvis_git_details → jarvis_memory_organizer}/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/top_level.txt +0 -0
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
import fcntl
|
|
3
2
|
import os
|
|
4
|
-
import
|
|
5
|
-
import select
|
|
6
|
-
import signal
|
|
3
|
+
import sys
|
|
7
4
|
import time
|
|
8
|
-
from typing import Any, Dict
|
|
5
|
+
from typing import Any, Dict, TYPE_CHECKING
|
|
6
|
+
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
7
|
+
|
|
8
|
+
# 为了类型检查,总是导入这些模块
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
# 平台相关的导入
|
|
13
|
+
if sys.platform != "win32":
|
|
14
|
+
pass
|
|
15
|
+
else:
|
|
16
|
+
# Windows平台的导入
|
|
17
|
+
pass
|
|
9
18
|
|
|
10
19
|
|
|
11
20
|
class VirtualTTYTool:
|
|
@@ -14,6 +23,7 @@ class VirtualTTYTool:
|
|
|
14
23
|
"控制虚拟终端执行各种操作,如启动终端、输入命令、获取输出等。"
|
|
15
24
|
+ "与execute_script不同,此工具会创建一个持久的虚拟终端会话,可以连续执行多个命令,并保持终端状态。"
|
|
16
25
|
+ "适用于需要交互式操作的场景,如运行需要用户输入的交互式程序(如:ssh连接、sftp传输、gdb/dlv调试等)。"
|
|
26
|
+
+ "注意:Windows平台功能有限,某些Unix特有功能可能不可用。"
|
|
17
27
|
)
|
|
18
28
|
parameters = {
|
|
19
29
|
"type": "object",
|
|
@@ -76,11 +86,21 @@ class VirtualTTYTool:
|
|
|
76
86
|
|
|
77
87
|
# 如果指定的tty_id不存在,为其创建一个新的tty_data
|
|
78
88
|
if tty_id not in agent.tty_sessions:
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
89
|
+
if sys.platform == "win32":
|
|
90
|
+
import queue as _queue # pylint: disable=import-outside-toplevel
|
|
91
|
+
|
|
92
|
+
agent.tty_sessions[tty_id] = {
|
|
93
|
+
"process": None,
|
|
94
|
+
"output_queue": _queue.Queue(),
|
|
95
|
+
"output_thread": None,
|
|
96
|
+
"shell": "cmd.exe",
|
|
97
|
+
}
|
|
98
|
+
else:
|
|
99
|
+
agent.tty_sessions[tty_id] = {
|
|
100
|
+
"master_fd": None,
|
|
101
|
+
"pid": None,
|
|
102
|
+
"shell": "/bin/bash",
|
|
103
|
+
}
|
|
84
104
|
|
|
85
105
|
action = args.get("action", "").strip().lower()
|
|
86
106
|
|
|
@@ -96,62 +116,62 @@ class VirtualTTYTool:
|
|
|
96
116
|
try:
|
|
97
117
|
if action == "launch":
|
|
98
118
|
if args.get("keys", "") != "":
|
|
99
|
-
print(
|
|
119
|
+
PrettyOutput.print(
|
|
120
|
+
"启动虚拟终端时,不能同时指定 keys 参数", OutputType.ERROR
|
|
121
|
+
)
|
|
100
122
|
return {
|
|
101
123
|
"success": False,
|
|
102
124
|
"stdout": "",
|
|
103
125
|
"stderr": "启动虚拟终端时,不能同时指定keys参数",
|
|
104
126
|
}
|
|
105
|
-
|
|
127
|
+
|
|
106
128
|
result = self._launch_tty(agent, tty_id)
|
|
107
|
-
if result["success"]:
|
|
108
|
-
print(
|
|
109
|
-
|
|
110
|
-
|
|
129
|
+
if not result["success"]:
|
|
130
|
+
PrettyOutput.print(
|
|
131
|
+
f"启动虚拟终端 [{tty_id}] 失败", OutputType.ERROR
|
|
132
|
+
)
|
|
111
133
|
return result
|
|
112
134
|
elif action == "send_keys":
|
|
113
135
|
keys = args.get("keys", "").strip()
|
|
114
136
|
add_enter = args.get("add_enter", True)
|
|
115
137
|
timeout = args.get("timeout", 5.0) # 默认5秒超时
|
|
116
|
-
|
|
138
|
+
|
|
117
139
|
result = self._input_command(agent, tty_id, keys, timeout, add_enter)
|
|
118
|
-
if result["success"]:
|
|
119
|
-
print(
|
|
120
|
-
|
|
121
|
-
|
|
140
|
+
if not result["success"]:
|
|
141
|
+
PrettyOutput.print(
|
|
142
|
+
f"发送按键序列到终端 [{tty_id}] 失败", OutputType.ERROR
|
|
143
|
+
)
|
|
122
144
|
return result
|
|
123
145
|
elif action == "output":
|
|
124
146
|
timeout = args.get("timeout", 5.0) # 默认5秒超时
|
|
125
|
-
|
|
147
|
+
|
|
126
148
|
result = self._get_output(agent, tty_id, timeout)
|
|
127
|
-
if result["success"]:
|
|
128
|
-
print(
|
|
129
|
-
|
|
130
|
-
|
|
149
|
+
if not result["success"]:
|
|
150
|
+
PrettyOutput.print(
|
|
151
|
+
f"获取终端 [{tty_id}] 输出失败", OutputType.ERROR
|
|
152
|
+
)
|
|
131
153
|
return result
|
|
132
154
|
elif action == "close":
|
|
133
|
-
|
|
155
|
+
|
|
134
156
|
result = self._close_tty(agent, tty_id)
|
|
135
|
-
if result["success"]:
|
|
136
|
-
print(
|
|
137
|
-
|
|
138
|
-
|
|
157
|
+
if not result["success"]:
|
|
158
|
+
PrettyOutput.print(
|
|
159
|
+
f"关闭虚拟终端 [{tty_id}] 失败", OutputType.ERROR
|
|
160
|
+
)
|
|
139
161
|
return result
|
|
140
162
|
elif action == "get_screen":
|
|
141
|
-
|
|
163
|
+
|
|
142
164
|
result = self._get_screen(agent, tty_id)
|
|
143
|
-
if result["success"]:
|
|
144
|
-
print(
|
|
145
|
-
|
|
146
|
-
|
|
165
|
+
if not result["success"]:
|
|
166
|
+
PrettyOutput.print(
|
|
167
|
+
f"获取终端 [{tty_id}] 屏幕内容失败", OutputType.ERROR
|
|
168
|
+
)
|
|
147
169
|
return result
|
|
148
170
|
elif action == "list":
|
|
149
|
-
|
|
171
|
+
|
|
150
172
|
result = self._list_ttys(agent)
|
|
151
|
-
if result["success"]:
|
|
152
|
-
print("
|
|
153
|
-
else:
|
|
154
|
-
print("❌ 获取虚拟终端列表失败")
|
|
173
|
+
if not result["success"]:
|
|
174
|
+
PrettyOutput.print("获取虚拟终端列表失败", OutputType.ERROR)
|
|
155
175
|
return result
|
|
156
176
|
return {"success": False, "stdout": "", "stderr": "不支持的操作"}
|
|
157
177
|
|
|
@@ -164,13 +184,25 @@ class VirtualTTYTool:
|
|
|
164
184
|
|
|
165
185
|
def _launch_tty(self, agent: Any, tty_id: str) -> Dict[str, Any]:
|
|
166
186
|
"""启动虚拟终端"""
|
|
187
|
+
if sys.platform == "win32":
|
|
188
|
+
return self._launch_tty_windows(agent, tty_id)
|
|
189
|
+
else:
|
|
190
|
+
return self._launch_tty_unix(agent, tty_id)
|
|
191
|
+
|
|
192
|
+
def _launch_tty_unix(self, agent: Any, tty_id: str) -> Dict[str, Any]:
|
|
193
|
+
"""Unix/Linux平台启动虚拟终端"""
|
|
167
194
|
try:
|
|
168
195
|
# 如果该ID的终端已经启动,先关闭它
|
|
169
196
|
if agent.tty_sessions[tty_id]["master_fd"] is not None:
|
|
170
197
|
self._close_tty(agent, tty_id)
|
|
171
198
|
|
|
199
|
+
# 在Unix平台上导入需要的模块
|
|
200
|
+
import pty as _pty # pylint: disable=import-outside-toplevel
|
|
201
|
+
import fcntl as _fcntl # pylint: disable=import-outside-toplevel
|
|
202
|
+
import select as _select # pylint: disable=import-outside-toplevel
|
|
203
|
+
|
|
172
204
|
# 创建伪终端
|
|
173
|
-
pid, master_fd =
|
|
205
|
+
pid, master_fd = _pty.fork()
|
|
174
206
|
|
|
175
207
|
if pid == 0: # 子进程
|
|
176
208
|
# 执行shell
|
|
@@ -180,7 +212,7 @@ class VirtualTTYTool:
|
|
|
180
212
|
)
|
|
181
213
|
else: # 父进程
|
|
182
214
|
# 设置非阻塞模式
|
|
183
|
-
|
|
215
|
+
_fcntl.fcntl(master_fd, _fcntl.F_SETFL, os.O_NONBLOCK)
|
|
184
216
|
|
|
185
217
|
# 保存终端状态
|
|
186
218
|
agent.tty_sessions[tty_id]["master_fd"] = master_fd
|
|
@@ -191,7 +223,7 @@ class VirtualTTYTool:
|
|
|
191
223
|
start_time = time.time()
|
|
192
224
|
while time.time() - start_time < 2.0: # 最多等待2秒
|
|
193
225
|
try:
|
|
194
|
-
r, _, _ =
|
|
226
|
+
r, _, _ = _select.select([master_fd], [], [], 0.1)
|
|
195
227
|
if r:
|
|
196
228
|
data = os.read(master_fd, 1024)
|
|
197
229
|
if data:
|
|
@@ -199,9 +231,6 @@ class VirtualTTYTool:
|
|
|
199
231
|
except BlockingIOError:
|
|
200
232
|
continue
|
|
201
233
|
|
|
202
|
-
if output:
|
|
203
|
-
print(f"📤 终端 [{tty_id}]: {output}")
|
|
204
|
-
|
|
205
234
|
return {"success": True, "stdout": output, "stderr": ""}
|
|
206
235
|
|
|
207
236
|
except Exception as e:
|
|
@@ -211,6 +240,71 @@ class VirtualTTYTool:
|
|
|
211
240
|
"stderr": f"启动虚拟终端 [{tty_id}] 失败: {str(e)}",
|
|
212
241
|
}
|
|
213
242
|
|
|
243
|
+
def _launch_tty_windows(self, agent: Any, tty_id: str) -> Dict[str, Any]:
|
|
244
|
+
"""Windows平台启动虚拟终端"""
|
|
245
|
+
try:
|
|
246
|
+
# 如果该ID的终端已经启动,先关闭它
|
|
247
|
+
if agent.tty_sessions[tty_id]["process"] is not None:
|
|
248
|
+
self._close_tty(agent, tty_id)
|
|
249
|
+
|
|
250
|
+
# 在Windows平台上导入需要的模块
|
|
251
|
+
import subprocess as _subprocess # pylint: disable=import-outside-toplevel
|
|
252
|
+
import threading as _threading # pylint: disable=import-outside-toplevel
|
|
253
|
+
import queue as _queue # pylint: disable=import-outside-toplevel
|
|
254
|
+
|
|
255
|
+
# 创建子进程
|
|
256
|
+
process = _subprocess.Popen(
|
|
257
|
+
agent.tty_sessions[tty_id]["shell"],
|
|
258
|
+
stdin=_subprocess.PIPE,
|
|
259
|
+
stdout=_subprocess.PIPE,
|
|
260
|
+
stderr=_subprocess.STDOUT,
|
|
261
|
+
shell=True,
|
|
262
|
+
text=True,
|
|
263
|
+
bufsize=0,
|
|
264
|
+
encoding="utf-8",
|
|
265
|
+
errors="replace",
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
# 保存进程对象
|
|
269
|
+
agent.tty_sessions[tty_id]["process"] = process
|
|
270
|
+
|
|
271
|
+
# 创建输出读取线程
|
|
272
|
+
def read_output():
|
|
273
|
+
while True:
|
|
274
|
+
if process is None or process.poll() is not None:
|
|
275
|
+
break
|
|
276
|
+
try:
|
|
277
|
+
if process.stdout is None:
|
|
278
|
+
break
|
|
279
|
+
line = process.stdout.readline()
|
|
280
|
+
if line:
|
|
281
|
+
agent.tty_sessions[tty_id]["output_queue"].put(line)
|
|
282
|
+
except Exception:
|
|
283
|
+
break
|
|
284
|
+
|
|
285
|
+
output_thread = _threading.Thread(target=read_output, daemon=True)
|
|
286
|
+
output_thread.start()
|
|
287
|
+
agent.tty_sessions[tty_id]["output_thread"] = output_thread
|
|
288
|
+
|
|
289
|
+
# 读取初始输出
|
|
290
|
+
output = ""
|
|
291
|
+
start_time = time.time()
|
|
292
|
+
while time.time() - start_time < 2.0: # 最多等待2秒
|
|
293
|
+
try:
|
|
294
|
+
line = agent.tty_sessions[tty_id]["output_queue"].get(timeout=0.1)
|
|
295
|
+
output += line
|
|
296
|
+
except _queue.Empty:
|
|
297
|
+
continue
|
|
298
|
+
|
|
299
|
+
return {"success": True, "stdout": output, "stderr": ""}
|
|
300
|
+
|
|
301
|
+
except Exception as e:
|
|
302
|
+
return {
|
|
303
|
+
"success": False,
|
|
304
|
+
"stdout": "",
|
|
305
|
+
"stderr": f"启动虚拟终端 [{tty_id}] 失败: {str(e)}",
|
|
306
|
+
}
|
|
307
|
+
|
|
214
308
|
def _input_command(
|
|
215
309
|
self,
|
|
216
310
|
agent: Any,
|
|
@@ -225,6 +319,22 @@ class VirtualTTYTool:
|
|
|
225
319
|
command: 要输入的单行命令
|
|
226
320
|
add_enter: 是否在命令末尾添加回车符
|
|
227
321
|
"""
|
|
322
|
+
if sys.platform == "win32":
|
|
323
|
+
return self._input_command_windows(
|
|
324
|
+
agent, tty_id, command, timeout, add_enter
|
|
325
|
+
)
|
|
326
|
+
else:
|
|
327
|
+
return self._input_command_unix(agent, tty_id, command, timeout, add_enter)
|
|
328
|
+
|
|
329
|
+
def _input_command_unix(
|
|
330
|
+
self,
|
|
331
|
+
agent: Any,
|
|
332
|
+
tty_id: str,
|
|
333
|
+
command: str,
|
|
334
|
+
timeout: float,
|
|
335
|
+
add_enter: bool = True,
|
|
336
|
+
) -> Dict[str, Any]:
|
|
337
|
+
"""Unix/Linux平台输入命令"""
|
|
228
338
|
if agent.tty_sessions[tty_id]["master_fd"] is None:
|
|
229
339
|
return {
|
|
230
340
|
"success": False,
|
|
@@ -251,7 +361,9 @@ class VirtualTTYTool:
|
|
|
251
361
|
while time.time() - start_time < timeout:
|
|
252
362
|
try:
|
|
253
363
|
# 使用select等待数据可读
|
|
254
|
-
|
|
364
|
+
import select as _select # pylint: disable=import-outside-toplevel
|
|
365
|
+
|
|
366
|
+
r, _, _ = _select.select(
|
|
255
367
|
[agent.tty_sessions[tty_id]["master_fd"]], [], [], 0.1
|
|
256
368
|
)
|
|
257
369
|
if r:
|
|
@@ -260,7 +372,54 @@ class VirtualTTYTool:
|
|
|
260
372
|
output += data.decode()
|
|
261
373
|
except BlockingIOError:
|
|
262
374
|
continue
|
|
263
|
-
|
|
375
|
+
return {"success": True, "stdout": output, "stderr": ""}
|
|
376
|
+
|
|
377
|
+
except Exception as e:
|
|
378
|
+
return {
|
|
379
|
+
"success": False,
|
|
380
|
+
"stdout": "",
|
|
381
|
+
"stderr": f"在终端 [{tty_id}] 执行命令失败: {str(e)}",
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
def _input_command_windows(
|
|
385
|
+
self,
|
|
386
|
+
agent: Any,
|
|
387
|
+
tty_id: str,
|
|
388
|
+
command: str,
|
|
389
|
+
timeout: float,
|
|
390
|
+
add_enter: bool = True,
|
|
391
|
+
) -> Dict[str, Any]:
|
|
392
|
+
"""Windows平台输入命令"""
|
|
393
|
+
if agent.tty_sessions[tty_id]["process"] is None:
|
|
394
|
+
return {
|
|
395
|
+
"success": False,
|
|
396
|
+
"stdout": "",
|
|
397
|
+
"stderr": f"虚拟终端 [{tty_id}] 未启动",
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
# 严格检查并拒绝多行输入
|
|
401
|
+
if "\n" in command:
|
|
402
|
+
return {"success": False, "stdout": "", "stderr": "错误:禁止多行输入"}
|
|
403
|
+
|
|
404
|
+
try:
|
|
405
|
+
# 根据add_enter参数决定是否添加回车符
|
|
406
|
+
if add_enter:
|
|
407
|
+
command = command + "\n"
|
|
408
|
+
|
|
409
|
+
# 发送命令
|
|
410
|
+
agent.tty_sessions[tty_id]["process"].stdin.write(command)
|
|
411
|
+
agent.tty_sessions[tty_id]["process"].stdin.flush()
|
|
412
|
+
|
|
413
|
+
# 等待输出
|
|
414
|
+
output = ""
|
|
415
|
+
start_time = time.time()
|
|
416
|
+
while time.time() - start_time < timeout:
|
|
417
|
+
try:
|
|
418
|
+
line = agent.tty_sessions[tty_id]["output_queue"].get(timeout=0.1)
|
|
419
|
+
output += line
|
|
420
|
+
except Exception: # queue.Empty
|
|
421
|
+
continue
|
|
422
|
+
|
|
264
423
|
return {"success": True, "stdout": output, "stderr": ""}
|
|
265
424
|
|
|
266
425
|
except Exception as e:
|
|
@@ -274,6 +433,15 @@ class VirtualTTYTool:
|
|
|
274
433
|
self, agent: Any, tty_id: str, timeout: float = 5.0
|
|
275
434
|
) -> Dict[str, Any]:
|
|
276
435
|
"""获取终端输出"""
|
|
436
|
+
if sys.platform == "win32":
|
|
437
|
+
return self._get_output_windows(agent, tty_id, timeout)
|
|
438
|
+
else:
|
|
439
|
+
return self._get_output_unix(agent, tty_id, timeout)
|
|
440
|
+
|
|
441
|
+
def _get_output_unix(
|
|
442
|
+
self, agent: Any, tty_id: str, timeout: float = 5.0
|
|
443
|
+
) -> Dict[str, Any]:
|
|
444
|
+
"""Unix/Linux平台获取输出"""
|
|
277
445
|
if agent.tty_sessions[tty_id]["master_fd"] is None:
|
|
278
446
|
return {
|
|
279
447
|
"success": False,
|
|
@@ -287,7 +455,9 @@ class VirtualTTYTool:
|
|
|
287
455
|
|
|
288
456
|
while time.time() - start_time < timeout:
|
|
289
457
|
# 使用select等待数据可读
|
|
290
|
-
|
|
458
|
+
import select as _select # pylint: disable=import-outside-toplevel
|
|
459
|
+
|
|
460
|
+
r, _, _ = _select.select(
|
|
291
461
|
[agent.tty_sessions[tty_id]["master_fd"]], [], [], 0.1
|
|
292
462
|
)
|
|
293
463
|
if r:
|
|
@@ -302,7 +472,36 @@ class VirtualTTYTool:
|
|
|
302
472
|
break
|
|
303
473
|
except BlockingIOError:
|
|
304
474
|
break
|
|
305
|
-
|
|
475
|
+
return {"success": True, "stdout": output, "stderr": ""}
|
|
476
|
+
|
|
477
|
+
except Exception as e:
|
|
478
|
+
return {
|
|
479
|
+
"success": False,
|
|
480
|
+
"stdout": "",
|
|
481
|
+
"stderr": f"获取终端 [{tty_id}] 输出失败: {str(e)}",
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
def _get_output_windows(
|
|
485
|
+
self, agent: Any, tty_id: str, timeout: float = 5.0
|
|
486
|
+
) -> Dict[str, Any]:
|
|
487
|
+
"""Windows平台获取输出"""
|
|
488
|
+
if agent.tty_sessions[tty_id]["process"] is None:
|
|
489
|
+
return {
|
|
490
|
+
"success": False,
|
|
491
|
+
"stdout": "",
|
|
492
|
+
"stderr": f"虚拟终端 [{tty_id}] 未启动",
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
try:
|
|
496
|
+
output = ""
|
|
497
|
+
start_time = time.time()
|
|
498
|
+
|
|
499
|
+
while time.time() - start_time < timeout:
|
|
500
|
+
try:
|
|
501
|
+
line = agent.tty_sessions[tty_id]["output_queue"].get(timeout=0.1)
|
|
502
|
+
output += line
|
|
503
|
+
except Exception: # queue.Empty
|
|
504
|
+
continue
|
|
306
505
|
|
|
307
506
|
return {"success": True, "stdout": output, "stderr": ""}
|
|
308
507
|
|
|
@@ -315,6 +514,13 @@ class VirtualTTYTool:
|
|
|
315
514
|
|
|
316
515
|
def _close_tty(self, agent: Any, tty_id: str) -> Dict[str, Any]:
|
|
317
516
|
"""关闭虚拟终端"""
|
|
517
|
+
if sys.platform == "win32":
|
|
518
|
+
return self._close_tty_windows(agent, tty_id)
|
|
519
|
+
else:
|
|
520
|
+
return self._close_tty_unix(agent, tty_id)
|
|
521
|
+
|
|
522
|
+
def _close_tty_unix(self, agent: Any, tty_id: str) -> Dict[str, Any]:
|
|
523
|
+
"""Unix/Linux平台关闭终端"""
|
|
318
524
|
if agent.tty_sessions[tty_id]["master_fd"] is None:
|
|
319
525
|
return {
|
|
320
526
|
"success": True,
|
|
@@ -328,7 +534,9 @@ class VirtualTTYTool:
|
|
|
328
534
|
|
|
329
535
|
# 终止子进程
|
|
330
536
|
if agent.tty_sessions[tty_id]["pid"]:
|
|
331
|
-
|
|
537
|
+
import signal as _signal # pylint: disable=import-outside-toplevel
|
|
538
|
+
|
|
539
|
+
os.kill(agent.tty_sessions[tty_id]["pid"], _signal.SIGTERM)
|
|
332
540
|
|
|
333
541
|
# 重置终端数据
|
|
334
542
|
agent.tty_sessions[tty_id] = {
|
|
@@ -350,8 +558,53 @@ class VirtualTTYTool:
|
|
|
350
558
|
"stderr": f"关闭虚拟终端 [{tty_id}] 失败: {str(e)}",
|
|
351
559
|
}
|
|
352
560
|
|
|
561
|
+
def _close_tty_windows(self, agent: Any, tty_id: str) -> Dict[str, Any]:
|
|
562
|
+
"""Windows平台关闭终端"""
|
|
563
|
+
if agent.tty_sessions[tty_id]["process"] is None:
|
|
564
|
+
return {
|
|
565
|
+
"success": True,
|
|
566
|
+
"stdout": f"没有正在运行的虚拟终端 [{tty_id}]",
|
|
567
|
+
"stderr": "",
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
try:
|
|
571
|
+
# 终止进程
|
|
572
|
+
agent.tty_sessions[tty_id]["process"].terminate()
|
|
573
|
+
agent.tty_sessions[tty_id]["process"].wait()
|
|
574
|
+
|
|
575
|
+
# 重置终端数据
|
|
576
|
+
import queue as _queue # pylint: disable=import-outside-toplevel
|
|
577
|
+
|
|
578
|
+
agent.tty_sessions[tty_id] = {
|
|
579
|
+
"process": None,
|
|
580
|
+
"output_queue": _queue.Queue(),
|
|
581
|
+
"output_thread": None,
|
|
582
|
+
"shell": "cmd.exe",
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
return {
|
|
586
|
+
"success": True,
|
|
587
|
+
"stdout": f"虚拟终端 [{tty_id}] 已关闭",
|
|
588
|
+
"stderr": "",
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
except Exception as e:
|
|
592
|
+
return {
|
|
593
|
+
"success": False,
|
|
594
|
+
"stdout": "",
|
|
595
|
+
"stderr": f"关闭虚拟终端 [{tty_id}] 失败: {str(e)}",
|
|
596
|
+
}
|
|
597
|
+
|
|
353
598
|
def _get_screen(self, agent: Any, tty_id: str) -> Dict[str, Any]:
|
|
354
599
|
"""获取当前终端屏幕内容"""
|
|
600
|
+
if sys.platform == "win32":
|
|
601
|
+
# Windows平台暂不支持获取屏幕内容
|
|
602
|
+
return {
|
|
603
|
+
"success": False,
|
|
604
|
+
"stdout": "",
|
|
605
|
+
"stderr": "Windows平台暂不支持获取屏幕内容功能",
|
|
606
|
+
}
|
|
607
|
+
|
|
355
608
|
if agent.tty_sessions[tty_id]["master_fd"] is None:
|
|
356
609
|
return {
|
|
357
610
|
"success": False,
|
|
@@ -371,7 +624,9 @@ class VirtualTTYTool:
|
|
|
371
624
|
start_time = time.time()
|
|
372
625
|
while time.time() - start_time < 2.0: # 最多等待2秒
|
|
373
626
|
try:
|
|
374
|
-
|
|
627
|
+
import select as _select # pylint: disable=import-outside-toplevel
|
|
628
|
+
|
|
629
|
+
r, _, _ = _select.select(
|
|
375
630
|
[agent.tty_sessions[tty_id]["master_fd"]], [], [], 0.1
|
|
376
631
|
)
|
|
377
632
|
if r:
|
|
@@ -404,15 +659,28 @@ class VirtualTTYTool:
|
|
|
404
659
|
active_ttys = []
|
|
405
660
|
|
|
406
661
|
for tty_id, tty_data in agent.tty_sessions.items():
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
662
|
+
if sys.platform == "win32":
|
|
663
|
+
status = "活动" if tty_data["process"] is not None else "关闭"
|
|
664
|
+
active_ttys.append(
|
|
665
|
+
{
|
|
666
|
+
"id": tty_id,
|
|
667
|
+
"status": status,
|
|
668
|
+
"pid": (
|
|
669
|
+
tty_data["process"].pid if tty_data["process"] else None
|
|
670
|
+
),
|
|
671
|
+
"shell": tty_data["shell"],
|
|
672
|
+
}
|
|
673
|
+
)
|
|
674
|
+
else:
|
|
675
|
+
status = "活动" if tty_data["master_fd"] is not None else "关闭"
|
|
676
|
+
active_ttys.append(
|
|
677
|
+
{
|
|
678
|
+
"id": tty_id,
|
|
679
|
+
"status": status,
|
|
680
|
+
"pid": tty_data["pid"] if tty_data["pid"] else None,
|
|
681
|
+
"shell": tty_data["shell"],
|
|
682
|
+
}
|
|
683
|
+
)
|
|
416
684
|
|
|
417
685
|
# 格式化输出
|
|
418
686
|
output = "虚拟终端列表:\n"
|
|
@@ -29,28 +29,27 @@ arguments:
|
|
|
29
29
|
""",
|
|
30
30
|
"description": "网页搜索",
|
|
31
31
|
},
|
|
32
|
-
|
|
33
32
|
"FindRelatedFiles": {
|
|
34
33
|
"append": False,
|
|
35
|
-
"template":
|
|
34
|
+
"template": """
|
|
36
35
|
请使用工具在当前目录下查找与以下功能相关的文件:
|
|
37
36
|
""",
|
|
38
37
|
},
|
|
39
38
|
"Dev": {
|
|
40
39
|
"append": False,
|
|
41
|
-
"template":
|
|
40
|
+
"template": """
|
|
42
41
|
请调用create_code_agent开发以下需求:
|
|
43
42
|
""",
|
|
44
43
|
},
|
|
45
44
|
"Fix": {
|
|
46
45
|
"append": False,
|
|
47
|
-
"template":
|
|
46
|
+
"template": """
|
|
48
47
|
请修复以下问题:
|
|
49
48
|
""",
|
|
50
49
|
},
|
|
51
50
|
"Check": {
|
|
52
51
|
"append": True,
|
|
53
|
-
"template":
|
|
52
|
+
"template": """
|
|
54
53
|
请使用静态检查工具检查当前代码,必须严格遵守工具调用格式。
|
|
55
54
|
|
|
56
55
|
检查要求:
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import platform
|
|
3
|
+
import subprocess
|
|
4
|
+
|
|
5
|
+
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def copy_to_clipboard(text: str) -> None:
|
|
9
|
+
"""将文本复制到剪贴板,支持Windows、macOS和Linux
|
|
10
|
+
|
|
11
|
+
参数:
|
|
12
|
+
text: 要复制的文本
|
|
13
|
+
"""
|
|
14
|
+
PrettyOutput.print("--- 剪贴板内容开始 ---", OutputType.INFO)
|
|
15
|
+
print(text)
|
|
16
|
+
PrettyOutput.print("--- 剪贴板内容结束 ---", OutputType.INFO)
|
|
17
|
+
|
|
18
|
+
system = platform.system()
|
|
19
|
+
|
|
20
|
+
# Windows系统
|
|
21
|
+
if system == "Windows":
|
|
22
|
+
try:
|
|
23
|
+
# 使用Windows的clip命令
|
|
24
|
+
process = subprocess.Popen(
|
|
25
|
+
["clip"],
|
|
26
|
+
stdin=subprocess.PIPE,
|
|
27
|
+
stdout=subprocess.DEVNULL,
|
|
28
|
+
stderr=subprocess.DEVNULL,
|
|
29
|
+
shell=True,
|
|
30
|
+
)
|
|
31
|
+
if process.stdin:
|
|
32
|
+
process.stdin.write(text.encode("utf-8"))
|
|
33
|
+
process.stdin.close()
|
|
34
|
+
return
|
|
35
|
+
except Exception as e:
|
|
36
|
+
PrettyOutput.print(f"使用Windows clip命令时出错: {e}", OutputType.WARNING)
|
|
37
|
+
|
|
38
|
+
# macOS系统
|
|
39
|
+
elif system == "Darwin":
|
|
40
|
+
try:
|
|
41
|
+
process = subprocess.Popen(
|
|
42
|
+
["pbcopy"],
|
|
43
|
+
stdin=subprocess.PIPE,
|
|
44
|
+
stdout=subprocess.DEVNULL,
|
|
45
|
+
stderr=subprocess.DEVNULL,
|
|
46
|
+
)
|
|
47
|
+
if process.stdin:
|
|
48
|
+
process.stdin.write(text.encode("utf-8"))
|
|
49
|
+
process.stdin.close()
|
|
50
|
+
return
|
|
51
|
+
except Exception as e:
|
|
52
|
+
PrettyOutput.print(f"使用macOS pbcopy命令时出错: {e}", OutputType.WARNING)
|
|
53
|
+
|
|
54
|
+
# Linux系统
|
|
55
|
+
else:
|
|
56
|
+
# 尝试使用 xsel
|
|
57
|
+
try:
|
|
58
|
+
process = subprocess.Popen(
|
|
59
|
+
["xsel", "-b", "-i"],
|
|
60
|
+
stdin=subprocess.PIPE,
|
|
61
|
+
stdout=subprocess.DEVNULL,
|
|
62
|
+
stderr=subprocess.DEVNULL,
|
|
63
|
+
)
|
|
64
|
+
if process.stdin:
|
|
65
|
+
process.stdin.write(text.encode("utf-8"))
|
|
66
|
+
process.stdin.close()
|
|
67
|
+
return
|
|
68
|
+
except FileNotFoundError:
|
|
69
|
+
pass # xsel 未安装,继续尝试下一个
|
|
70
|
+
except Exception as e:
|
|
71
|
+
PrettyOutput.print(f"使用xsel时出错: {e}", OutputType.WARNING)
|
|
72
|
+
|
|
73
|
+
# 尝试使用 xclip
|
|
74
|
+
try:
|
|
75
|
+
process = subprocess.Popen(
|
|
76
|
+
["xclip", "-selection", "clipboard"],
|
|
77
|
+
stdin=subprocess.PIPE,
|
|
78
|
+
stdout=subprocess.DEVNULL,
|
|
79
|
+
stderr=subprocess.DEVNULL,
|
|
80
|
+
)
|
|
81
|
+
if process.stdin:
|
|
82
|
+
process.stdin.write(text.encode("utf-8"))
|
|
83
|
+
process.stdin.close()
|
|
84
|
+
return
|
|
85
|
+
except FileNotFoundError:
|
|
86
|
+
PrettyOutput.print(
|
|
87
|
+
"xsel 和 xclip 均未安装, 无法复制到剪贴板", OutputType.WARNING
|
|
88
|
+
)
|
|
89
|
+
except Exception as e:
|
|
90
|
+
PrettyOutput.print(f"使用xclip时出错: {e}", OutputType.WARNING)
|