jarvis-ai-assistant 0.3.30__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 +289 -87
- jarvis/jarvis_agent/agent_manager.py +17 -8
- jarvis/jarvis_agent/edit_file_handler.py +374 -86
- jarvis/jarvis_agent/event_bus.py +1 -1
- jarvis/jarvis_agent/file_context_handler.py +79 -0
- jarvis/jarvis_agent/jarvis.py +601 -43
- jarvis/jarvis_agent/main.py +32 -2
- jarvis/jarvis_agent/rewrite_file_handler.py +141 -0
- jarvis/jarvis_agent/run_loop.py +38 -5
- jarvis/jarvis_agent/share_manager.py +8 -1
- jarvis/jarvis_agent/stdio_redirect.py +295 -0
- jarvis/jarvis_agent/task_analyzer.py +5 -2
- jarvis/jarvis_agent/task_planner.py +496 -0
- jarvis/jarvis_agent/utils.py +5 -1
- 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 +1171 -94
- 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 +270 -8
- jarvis/jarvis_code_agent/utils.py +142 -0
- jarvis/jarvis_code_analysis/code_review.py +483 -569
- jarvis/jarvis_data/config_schema.json +97 -8
- jarvis/jarvis_git_utils/git_commiter.py +38 -26
- jarvis/jarvis_mcp/sse_mcp_client.py +2 -2
- jarvis/jarvis_mcp/stdio_mcp_client.py +1 -1
- jarvis/jarvis_memory_organizer/memory_organizer.py +1 -1
- jarvis/jarvis_multi_agent/__init__.py +239 -25
- jarvis/jarvis_multi_agent/main.py +37 -1
- jarvis/jarvis_platform/base.py +103 -51
- jarvis/jarvis_platform/openai.py +26 -1
- jarvis/jarvis_platform/yuanbao.py +1 -1
- jarvis/jarvis_platform_manager/service.py +2 -2
- jarvis/jarvis_rag/cli.py +4 -4
- 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_stats/cli.py +1 -1
- jarvis/jarvis_stats/stats.py +1 -1
- jarvis/jarvis_stats/visualizer.py +1 -1
- jarvis/jarvis_tools/cli/main.py +1 -0
- jarvis/jarvis_tools/execute_script.py +46 -9
- jarvis/jarvis_tools/generate_new_tool.py +3 -1
- jarvis/jarvis_tools/read_code.py +275 -12
- jarvis/jarvis_tools/read_symbols.py +141 -0
- jarvis/jarvis_tools/read_webpage.py +5 -3
- jarvis/jarvis_tools/registry.py +73 -35
- jarvis/jarvis_tools/search_web.py +15 -11
- jarvis/jarvis_tools/sub_agent.py +24 -42
- jarvis/jarvis_tools/sub_code_agent.py +14 -13
- jarvis/jarvis_tools/virtual_tty.py +1 -1
- jarvis/jarvis_utils/config.py +187 -35
- jarvis/jarvis_utils/embedding.py +3 -0
- jarvis/jarvis_utils/git_utils.py +181 -6
- jarvis/jarvis_utils/globals.py +3 -3
- jarvis/jarvis_utils/http.py +1 -1
- jarvis/jarvis_utils/input.py +78 -2
- jarvis/jarvis_utils/methodology.py +25 -19
- jarvis/jarvis_utils/utils.py +644 -359
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/METADATA +85 -1
- jarvis_ai_assistant-0.7.0.dist-info/RECORD +192 -0
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/entry_points.txt +4 -0
- jarvis/jarvis_agent/config.py +0 -92
- jarvis/jarvis_tools/edit_file.py +0 -179
- jarvis/jarvis_tools/rewrite_file.py +0 -191
- jarvis_ai_assistant-0.3.30.dist-info/RECORD +0 -137
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Jarvis 安全分析套件 —— Workflow(含可复现直扫基线)
|
|
4
|
+
|
|
5
|
+
目标:
|
|
6
|
+
- 识别指定模块的安全问题(内存管理、缓冲区操作、错误处理等),检出率≥60% 为目标。
|
|
7
|
+
- 在不依赖外部服务的前提下,提供一个“可复现、可离线”的直扫基线(direct scan)。
|
|
8
|
+
- 当前采用“先直扫拆分子任务,再由单Agent逐条分析”的模式;保留接口便于后续切换。
|
|
9
|
+
|
|
10
|
+
本模块提供:
|
|
11
|
+
- direct_scan(entry_path, languages=None, exclude_dirs=None) -> Dict:纯Python+正则/命令行辅助扫描,生成结构化结果
|
|
12
|
+
- format_markdown_report(result_json: Dict) -> str:将结构化结果转为可读的 Markdown
|
|
13
|
+
|
|
14
|
+
- run_with_agent(entry_path, languages=None) -> str:使用单Agent逐条子任务分析模式(复用 jarvis.jarvis_sec.__init__ 的实现)
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from dataclasses import asdict
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Any, Dict, Iterable, List, Optional, cast
|
|
20
|
+
|
|
21
|
+
import typer
|
|
22
|
+
|
|
23
|
+
from jarvis.jarvis_sec.checkers import analyze_c_files, analyze_rust_files
|
|
24
|
+
from jarvis.jarvis_sec.types import Issue
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# ---------------------------
|
|
28
|
+
# 数据结构
|
|
29
|
+
# ---------------------------
|
|
30
|
+
|
|
31
|
+
# Issue dataclass is provided by jarvis.jarvis_sec.types to avoid circular imports
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# ---------------------------
|
|
35
|
+
# 工具函数
|
|
36
|
+
# ---------------------------
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _iter_source_files(
|
|
40
|
+
entry_path: str,
|
|
41
|
+
languages: Optional[List[str]] = None,
|
|
42
|
+
exclude_dirs: Optional[List[str]] = None,
|
|
43
|
+
) -> Iterable[Path]:
|
|
44
|
+
"""
|
|
45
|
+
递归枚举源文件,支持按扩展名过滤与目录排除。
|
|
46
|
+
默认语言扩展:c, cpp, h, hpp, rs
|
|
47
|
+
"""
|
|
48
|
+
entry = Path(entry_path)
|
|
49
|
+
if not entry.exists():
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
exts = set((languages or ["c", "cpp", "h", "hpp", "rs"]))
|
|
53
|
+
excludes = set(exclude_dirs or [".git", "build", "out", "target", "dist", "bin", "obj", "third_party", "vendor", "deps", "dependencies", "libs", "libraries", "external", "node_modules", "test", "tests", "__tests__", "spec", "testsuite", "testdata", "benchmark", "benchmarks", "perf", "performance", "bench", "benches", "profiling", "profiler", "example", "examples", "tmp", "temp", "cache", ".cache", "docs", "doc", "documentation", "generated", "gen", "mocks", "fixtures", "samples", "sample", "playground", "sandbox"])
|
|
54
|
+
|
|
55
|
+
for p in entry.rglob("*"):
|
|
56
|
+
if not p.is_file():
|
|
57
|
+
continue
|
|
58
|
+
# 目录排除(任意祖先包含即排除)
|
|
59
|
+
skip = False
|
|
60
|
+
for parent in p.parents:
|
|
61
|
+
if parent.name in excludes:
|
|
62
|
+
skip = True
|
|
63
|
+
break
|
|
64
|
+
if skip:
|
|
65
|
+
continue
|
|
66
|
+
|
|
67
|
+
suf = p.suffix.lstrip(".").lower()
|
|
68
|
+
if suf in exts:
|
|
69
|
+
yield p.relative_to(entry)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# ---------------------------
|
|
74
|
+
# 汇总与报告
|
|
75
|
+
# ---------------------------
|
|
76
|
+
|
|
77
|
+
def direct_scan(
|
|
78
|
+
entry_path: str,
|
|
79
|
+
languages: Optional[List[str]] = None,
|
|
80
|
+
exclude_dirs: Optional[List[str]] = None,
|
|
81
|
+
) -> Dict:
|
|
82
|
+
"""
|
|
83
|
+
直扫基线:对 C/C++/Rust 进行启发式扫描,输出结构化 JSON。
|
|
84
|
+
- 改进:委派至模块化检查器(oh_sec.checkers),统一规则与置信度模型。
|
|
85
|
+
"""
|
|
86
|
+
base = Path(entry_path).resolve()
|
|
87
|
+
# 计算实际使用的排除目录列表
|
|
88
|
+
default_excludes = [".git", "build", "out", "target", "dist", "bin", "obj", "third_party", "vendor", "deps", "dependencies", "libs", "libraries", "external", "node_modules", "test", "tests", "__tests__", "spec", "testsuite", "testdata", "benchmark", "benchmarks", "perf", "performance", "bench", "benches", "profiling", "profiler", "example", "examples", "tmp", "temp", "cache", ".cache", "docs", "doc", "documentation", "generated", "gen", "mocks", "fixtures", "samples", "sample", "playground", "sandbox"]
|
|
89
|
+
actual_excludes = exclude_dirs if exclude_dirs is not None else default_excludes
|
|
90
|
+
|
|
91
|
+
# 检查代码库中实际存在的排除目录
|
|
92
|
+
excludes_set = set(actual_excludes)
|
|
93
|
+
actual_excluded_dirs = []
|
|
94
|
+
for item in base.rglob("*"):
|
|
95
|
+
if item.is_dir() and item.name in excludes_set:
|
|
96
|
+
rel_path = item.relative_to(base)
|
|
97
|
+
if str(rel_path) not in actual_excluded_dirs:
|
|
98
|
+
actual_excluded_dirs.append(str(rel_path))
|
|
99
|
+
|
|
100
|
+
if actual_excluded_dirs:
|
|
101
|
+
typer.secho("[jarvis-sec] 实际排除的目录:", fg=typer.colors.BLUE)
|
|
102
|
+
for dir_path in sorted(actual_excluded_dirs):
|
|
103
|
+
typer.secho(f" - {dir_path}", fg=typer.colors.BLUE)
|
|
104
|
+
else:
|
|
105
|
+
typer.secho(f"[jarvis-sec] 未发现需要排除的目录(配置的排除目录: {', '.join(sorted(actual_excludes))})", fg=typer.colors.BLUE)
|
|
106
|
+
|
|
107
|
+
files = list(_iter_source_files(entry_path, languages, exclude_dirs))
|
|
108
|
+
|
|
109
|
+
# 按语言分组
|
|
110
|
+
c_like_exts = {".c", ".cpp", ".h", ".hpp"}
|
|
111
|
+
rust_exts = {".rs"}
|
|
112
|
+
c_files: List[Path] = [p for p in files if p.suffix.lower() in c_like_exts]
|
|
113
|
+
r_files: List[Path] = [p for p in files if p.suffix.lower() in rust_exts]
|
|
114
|
+
|
|
115
|
+
# 调用检查器(保持相对路径,基于 base_path 解析)
|
|
116
|
+
issues_c = analyze_c_files(str(base), [str(p) for p in c_files]) if c_files else []
|
|
117
|
+
issues_r = analyze_rust_files(str(base), [str(p) for p in r_files]) if r_files else []
|
|
118
|
+
issues: List[Issue] = issues_c + issues_r
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
summary: Dict[str, Any] = {
|
|
122
|
+
"total": len(issues),
|
|
123
|
+
"by_language": {"c/cpp": 0, "rust": 0},
|
|
124
|
+
"by_category": {},
|
|
125
|
+
"top_risk_files": [],
|
|
126
|
+
"scanned_files": len(files),
|
|
127
|
+
"scanned_root": str(base),
|
|
128
|
+
}
|
|
129
|
+
file_score: Dict[str, int] = {}
|
|
130
|
+
# Safely update language/category counts with explicit typing
|
|
131
|
+
lang_counts = cast(Dict[str, int], summary["by_language"])
|
|
132
|
+
cat_counts = cast(Dict[str, int], summary["by_category"])
|
|
133
|
+
for it in issues:
|
|
134
|
+
lang_counts[it.language] = lang_counts.get(it.language, 0) + 1
|
|
135
|
+
cat_counts[it.category] = cat_counts.get(it.category, 0) + 1
|
|
136
|
+
file_score[it.file] = file_score.get(it.file, 0) + 1
|
|
137
|
+
# Top 风险文件
|
|
138
|
+
summary["top_risk_files"] = [f for f, _ in sorted(file_score.items(), key=lambda x: x[1], reverse=True)[:10]]
|
|
139
|
+
|
|
140
|
+
result = {
|
|
141
|
+
"summary": summary,
|
|
142
|
+
"issues": [asdict(i) for i in issues],
|
|
143
|
+
}
|
|
144
|
+
return result
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def format_markdown_report(result_json: Dict) -> str:
|
|
148
|
+
"""
|
|
149
|
+
将结构化 JSON 转为 Markdown 可读报告。
|
|
150
|
+
"""
|
|
151
|
+
s = result_json.get("summary", {})
|
|
152
|
+
issues: List[Dict] = result_json.get("issues", [])
|
|
153
|
+
md: List[str] = []
|
|
154
|
+
md.append("# Jarvis 安全问题分析报告(直扫基线)")
|
|
155
|
+
md.append("")
|
|
156
|
+
md.append(f"- 扫描根目录: {s.get('scanned_root', '')}")
|
|
157
|
+
md.append(f"- 扫描文件数: {s.get('scanned_files', 0)}")
|
|
158
|
+
md.append(f"- 检出问题总数: {s.get('total', 0)}")
|
|
159
|
+
md.append("")
|
|
160
|
+
md.append("## 统计概览")
|
|
161
|
+
by_lang = s.get("by_language", {})
|
|
162
|
+
md.append(f"- 按语言: c/cpp={by_lang.get('c/cpp', 0)}, rust={by_lang.get('rust', 0)}")
|
|
163
|
+
md.append("- 按类别:")
|
|
164
|
+
for k, v in s.get("by_category", {}).items():
|
|
165
|
+
md.append(f" - {k}: {v}")
|
|
166
|
+
if s.get("top_risk_files"):
|
|
167
|
+
md.append("- Top 风险文件:")
|
|
168
|
+
for f in s["top_risk_files"]:
|
|
169
|
+
md.append(f" - {f}")
|
|
170
|
+
md.append("")
|
|
171
|
+
md.append("## 详细问题")
|
|
172
|
+
for i, it in enumerate(issues, start=1):
|
|
173
|
+
md.append(f"### [{i}] {it.get('file')}:{it.get('line')} ({it.get('language')}, {it.get('category')})")
|
|
174
|
+
md.append(f"- 模式: {it.get('pattern')}")
|
|
175
|
+
md.append(f"- 证据: `{it.get('evidence')}`")
|
|
176
|
+
md.append(f"- 描述: {it.get('description')}")
|
|
177
|
+
md.append(f"- 建议: {it.get('suggestion')}")
|
|
178
|
+
md.append(f"- 置信度: {it.get('confidence')}, 严重性: {it.get('severity')}")
|
|
179
|
+
md.append("")
|
|
180
|
+
return "\n".join(md)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def run_with_agent(
|
|
184
|
+
entry_path: str,
|
|
185
|
+
languages: Optional[List[str]] = None,
|
|
186
|
+
llm_group: Optional[str] = None,
|
|
187
|
+
report_file: Optional[str] = None,
|
|
188
|
+
cluster_limit: int = 50,
|
|
189
|
+
exclude_dirs: Optional[List[str]] = None,
|
|
190
|
+
) -> str:
|
|
191
|
+
"""
|
|
192
|
+
使用单Agent逐条子任务分析模式运行(与 jarvis.jarvis_sec.__init__ 中保持一致)。
|
|
193
|
+
- 先执行本地直扫,生成候选问题
|
|
194
|
+
- 为每条候选创建一次普通Agent任务进行分析与验证
|
|
195
|
+
- 聚合为最终报告(JSON + Markdown)返回
|
|
196
|
+
|
|
197
|
+
其他:
|
|
198
|
+
- llm_group: 本次分析使用的模型组(仅透传给 Agent,不修改全局配置)
|
|
199
|
+
- report_file: JSONL 报告文件路径(可选,透传)
|
|
200
|
+
- cluster_limit: 聚类时每批次最多处理的告警数(默认 50),当单个文件告警过多时按批次进行聚类
|
|
201
|
+
- exclude_dirs: 要排除的目录列表(可选),默认已包含构建产物(build, out, target, dist, bin, obj)、依赖目录(third_party, vendor, deps, dependencies, libs, libraries, external, node_modules)、测试目录(test, tests, __tests__, spec, testsuite, testdata)、性能测试目录(benchmark, benchmarks, perf, performance, bench, benches, profiling, profiler)、示例目录(example, examples)、临时/缓存(tmp, temp, cache, .cache)、文档(docs, doc, documentation)、生成代码(generated, gen)和其他(mocks, fixtures, samples, sample, playground, sandbox)
|
|
202
|
+
"""
|
|
203
|
+
from jarvis.jarvis_sec import run_security_analysis # 延迟导入,避免循环
|
|
204
|
+
return run_security_analysis(
|
|
205
|
+
entry_path,
|
|
206
|
+
languages=languages,
|
|
207
|
+
llm_group=llm_group,
|
|
208
|
+
report_file=report_file,
|
|
209
|
+
cluster_limit=cluster_limit,
|
|
210
|
+
exclude_dirs=exclude_dirs,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
__all__ = [
|
|
215
|
+
"Issue",
|
|
216
|
+
"direct_scan",
|
|
217
|
+
"format_markdown_report",
|
|
218
|
+
"run_with_agent",
|
|
219
|
+
]
|
jarvis/jarvis_stats/cli.py
CHANGED
jarvis/jarvis_stats/stats.py
CHANGED
jarvis/jarvis_tools/cli/main.py
CHANGED
|
@@ -19,6 +19,7 @@ class ScriptTool:
|
|
|
19
19
|
+ "注意:由于模型上下文长度限制,请避免在脚本中输出大量信息,应该使用rg过滤输出。"
|
|
20
20
|
+ "与virtual_tty不同,此工具会创建一个临时的脚本文件,并使用脚本命令执行脚本,不具备交互式操作的能力,"
|
|
21
21
|
+ "适用于需要执行脚本并获取结果的场景。不适合需要交互式操作的场景(如:ssh连接、sftp传输、gdb/dlv调试等)。"
|
|
22
|
+
+ "在非交互模式(JARVIS_NON_INTERACTIVE=true)下:脚本执行时间限制为5分钟,超时将被终止并返回超时信息;用户无法与命令交互,请不要执行需要交互的命令。"
|
|
22
23
|
)
|
|
23
24
|
parameters = {
|
|
24
25
|
"type": "object",
|
|
@@ -115,8 +116,37 @@ class ScriptTool:
|
|
|
115
116
|
f"script -q -c '{interpreter} {script_path}' {output_file}"
|
|
116
117
|
)
|
|
117
118
|
|
|
118
|
-
# Execute command
|
|
119
|
-
|
|
119
|
+
# Execute command with optional timeout in non-interactive mode
|
|
120
|
+
from jarvis.jarvis_utils.config import get_script_execution_timeout, is_non_interactive
|
|
121
|
+
import subprocess
|
|
122
|
+
|
|
123
|
+
timed_out = False
|
|
124
|
+
if is_non_interactive():
|
|
125
|
+
try:
|
|
126
|
+
proc = subprocess.Popen(tee_command, shell=True)
|
|
127
|
+
try:
|
|
128
|
+
proc.wait(timeout=get_script_execution_timeout())
|
|
129
|
+
except subprocess.TimeoutExpired:
|
|
130
|
+
timed_out = True
|
|
131
|
+
try:
|
|
132
|
+
proc.kill()
|
|
133
|
+
except Exception:
|
|
134
|
+
pass
|
|
135
|
+
except Exception as e:
|
|
136
|
+
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
137
|
+
# Attempt to read any partial output if available
|
|
138
|
+
try:
|
|
139
|
+
output = self.get_display_output(output_file)
|
|
140
|
+
except Exception as ee:
|
|
141
|
+
output = f"读取输出文件失败: {str(ee)}"
|
|
142
|
+
return {
|
|
143
|
+
"success": False,
|
|
144
|
+
"stdout": output,
|
|
145
|
+
"stderr": f"执行脚本失败: {str(e)}",
|
|
146
|
+
}
|
|
147
|
+
else:
|
|
148
|
+
# Execute command and capture return code
|
|
149
|
+
os.system(tee_command)
|
|
120
150
|
|
|
121
151
|
# Read and process output file
|
|
122
152
|
try:
|
|
@@ -125,12 +155,19 @@ class ScriptTool:
|
|
|
125
155
|
except Exception as e:
|
|
126
156
|
output = f"读取输出文件失败: {str(e)}"
|
|
127
157
|
|
|
128
|
-
# Return
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
158
|
+
# Return result (handle timeout in non-interactive mode)
|
|
159
|
+
if is_non_interactive() and timed_out:
|
|
160
|
+
return {
|
|
161
|
+
"success": False,
|
|
162
|
+
"stdout": output,
|
|
163
|
+
"stderr": f"执行超时(超过{get_script_execution_timeout()}秒),进程已被终止(非交互模式)。",
|
|
164
|
+
}
|
|
165
|
+
else:
|
|
166
|
+
return {
|
|
167
|
+
"success": True,
|
|
168
|
+
"stdout": output,
|
|
169
|
+
"stderr": "",
|
|
170
|
+
}
|
|
134
171
|
|
|
135
172
|
finally:
|
|
136
173
|
# Clean up temporary files
|
|
@@ -173,7 +210,7 @@ class ScriptTool:
|
|
|
173
210
|
if __name__ == "__main__":
|
|
174
211
|
script_tool = ScriptTool()
|
|
175
212
|
PrettyOutput.print(
|
|
176
|
-
script_tool.get_display_output("/
|
|
213
|
+
script_tool.get_display_output("/path/to/a.txt"),
|
|
177
214
|
OutputType.CODE,
|
|
178
215
|
lang="text",
|
|
179
216
|
)
|
|
@@ -173,7 +173,9 @@ class generate_new_tool:
|
|
|
173
173
|
__import__(pkg)
|
|
174
174
|
except ImportError:
|
|
175
175
|
|
|
176
|
-
import subprocess
|
|
176
|
+
import subprocess
|
|
177
|
+
import sys
|
|
178
|
+
import os
|
|
177
179
|
from shutil import which as _which
|
|
178
180
|
# 优先使用 uv 安装(先查 venv 内 uv,再查 PATH 中 uv),否则回退到 python -m pip
|
|
179
181
|
if sys.platform == "win32":
|