jarvis-ai-assistant 0.7.16__py3-none-any.whl → 1.0.2__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 +567 -222
- jarvis/jarvis_agent/agent_manager.py +19 -12
- jarvis/jarvis_agent/builtin_input_handler.py +79 -11
- jarvis/jarvis_agent/config_editor.py +7 -2
- jarvis/jarvis_agent/event_bus.py +24 -13
- jarvis/jarvis_agent/events.py +19 -1
- jarvis/jarvis_agent/file_context_handler.py +67 -64
- jarvis/jarvis_agent/file_methodology_manager.py +38 -24
- jarvis/jarvis_agent/jarvis.py +186 -114
- jarvis/jarvis_agent/language_extractors/__init__.py +8 -1
- jarvis/jarvis_agent/language_extractors/c_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/cpp_extractor.py +9 -4
- jarvis/jarvis_agent/language_extractors/go_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/java_extractor.py +27 -20
- jarvis/jarvis_agent/language_extractors/javascript_extractor.py +22 -17
- jarvis/jarvis_agent/language_extractors/python_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/rust_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/typescript_extractor.py +22 -17
- jarvis/jarvis_agent/language_support_info.py +250 -219
- jarvis/jarvis_agent/main.py +19 -23
- jarvis/jarvis_agent/memory_manager.py +9 -6
- jarvis/jarvis_agent/methodology_share_manager.py +21 -15
- jarvis/jarvis_agent/output_handler.py +4 -2
- jarvis/jarvis_agent/prompt_builder.py +7 -6
- jarvis/jarvis_agent/prompt_manager.py +113 -8
- jarvis/jarvis_agent/prompts.py +317 -85
- jarvis/jarvis_agent/protocols.py +5 -2
- jarvis/jarvis_agent/run_loop.py +192 -32
- jarvis/jarvis_agent/session_manager.py +7 -3
- jarvis/jarvis_agent/share_manager.py +23 -13
- jarvis/jarvis_agent/shell_input_handler.py +12 -8
- jarvis/jarvis_agent/stdio_redirect.py +25 -26
- jarvis/jarvis_agent/task_analyzer.py +29 -23
- jarvis/jarvis_agent/task_list.py +869 -0
- jarvis/jarvis_agent/task_manager.py +26 -23
- jarvis/jarvis_agent/tool_executor.py +6 -5
- jarvis/jarvis_agent/tool_share_manager.py +24 -14
- jarvis/jarvis_agent/user_interaction.py +3 -3
- jarvis/jarvis_agent/utils.py +9 -1
- jarvis/jarvis_agent/web_bridge.py +37 -17
- jarvis/jarvis_agent/web_output_sink.py +5 -2
- jarvis/jarvis_agent/web_server.py +165 -36
- jarvis/jarvis_c2rust/__init__.py +1 -1
- jarvis/jarvis_c2rust/cli.py +260 -141
- jarvis/jarvis_c2rust/collector.py +37 -18
- jarvis/jarvis_c2rust/constants.py +60 -0
- jarvis/jarvis_c2rust/library_replacer.py +242 -1010
- jarvis/jarvis_c2rust/library_replacer_checkpoint.py +133 -0
- jarvis/jarvis_c2rust/library_replacer_llm.py +287 -0
- jarvis/jarvis_c2rust/library_replacer_loader.py +191 -0
- jarvis/jarvis_c2rust/library_replacer_output.py +134 -0
- jarvis/jarvis_c2rust/library_replacer_prompts.py +124 -0
- jarvis/jarvis_c2rust/library_replacer_utils.py +188 -0
- jarvis/jarvis_c2rust/llm_module_agent.py +98 -1044
- jarvis/jarvis_c2rust/llm_module_agent_apply.py +170 -0
- jarvis/jarvis_c2rust/llm_module_agent_executor.py +288 -0
- jarvis/jarvis_c2rust/llm_module_agent_loader.py +170 -0
- jarvis/jarvis_c2rust/llm_module_agent_prompts.py +268 -0
- jarvis/jarvis_c2rust/llm_module_agent_types.py +57 -0
- jarvis/jarvis_c2rust/llm_module_agent_utils.py +150 -0
- jarvis/jarvis_c2rust/llm_module_agent_validator.py +119 -0
- jarvis/jarvis_c2rust/loaders.py +28 -10
- jarvis/jarvis_c2rust/models.py +5 -2
- jarvis/jarvis_c2rust/optimizer.py +192 -1974
- jarvis/jarvis_c2rust/optimizer_build_fix.py +286 -0
- jarvis/jarvis_c2rust/optimizer_clippy.py +766 -0
- jarvis/jarvis_c2rust/optimizer_config.py +49 -0
- jarvis/jarvis_c2rust/optimizer_docs.py +183 -0
- jarvis/jarvis_c2rust/optimizer_options.py +48 -0
- jarvis/jarvis_c2rust/optimizer_progress.py +469 -0
- jarvis/jarvis_c2rust/optimizer_report.py +52 -0
- jarvis/jarvis_c2rust/optimizer_unsafe.py +309 -0
- jarvis/jarvis_c2rust/optimizer_utils.py +469 -0
- jarvis/jarvis_c2rust/optimizer_visibility.py +185 -0
- jarvis/jarvis_c2rust/scanner.py +229 -166
- jarvis/jarvis_c2rust/transpiler.py +531 -2732
- jarvis/jarvis_c2rust/transpiler_agents.py +503 -0
- jarvis/jarvis_c2rust/transpiler_build.py +1294 -0
- jarvis/jarvis_c2rust/transpiler_codegen.py +204 -0
- jarvis/jarvis_c2rust/transpiler_compile.py +146 -0
- jarvis/jarvis_c2rust/transpiler_config.py +178 -0
- jarvis/jarvis_c2rust/transpiler_context.py +122 -0
- jarvis/jarvis_c2rust/transpiler_executor.py +516 -0
- jarvis/jarvis_c2rust/transpiler_generation.py +278 -0
- jarvis/jarvis_c2rust/transpiler_git.py +163 -0
- jarvis/jarvis_c2rust/transpiler_mod_utils.py +225 -0
- jarvis/jarvis_c2rust/transpiler_modules.py +336 -0
- jarvis/jarvis_c2rust/transpiler_planning.py +394 -0
- jarvis/jarvis_c2rust/transpiler_review.py +1196 -0
- jarvis/jarvis_c2rust/transpiler_symbols.py +176 -0
- jarvis/jarvis_c2rust/utils.py +269 -79
- jarvis/jarvis_code_agent/after_change.py +233 -0
- jarvis/jarvis_code_agent/build_validation_config.py +37 -30
- jarvis/jarvis_code_agent/builtin_rules.py +68 -0
- jarvis/jarvis_code_agent/code_agent.py +976 -1517
- jarvis/jarvis_code_agent/code_agent_build.py +227 -0
- jarvis/jarvis_code_agent/code_agent_diff.py +246 -0
- jarvis/jarvis_code_agent/code_agent_git.py +525 -0
- jarvis/jarvis_code_agent/code_agent_impact.py +177 -0
- jarvis/jarvis_code_agent/code_agent_lint.py +283 -0
- jarvis/jarvis_code_agent/code_agent_llm.py +159 -0
- jarvis/jarvis_code_agent/code_agent_postprocess.py +105 -0
- jarvis/jarvis_code_agent/code_agent_prompts.py +46 -0
- jarvis/jarvis_code_agent/code_agent_rules.py +305 -0
- jarvis/jarvis_code_agent/code_analyzer/__init__.py +52 -48
- jarvis/jarvis_code_agent/code_analyzer/base_language.py +12 -10
- jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +12 -11
- jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +16 -12
- jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +26 -17
- jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +558 -104
- jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +27 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +22 -18
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +21 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +20 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +27 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +47 -23
- jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +71 -37
- jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +162 -35
- jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +111 -57
- jarvis/jarvis_code_agent/code_analyzer/build_validator.py +18 -12
- jarvis/jarvis_code_agent/code_analyzer/context_manager.py +185 -183
- jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +2 -1
- jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +24 -15
- jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +227 -141
- jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +321 -247
- jarvis/jarvis_code_agent/code_analyzer/language_registry.py +37 -29
- jarvis/jarvis_code_agent/code_analyzer/language_support.py +21 -13
- jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +15 -9
- jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +75 -45
- jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +87 -52
- jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +84 -51
- jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +94 -64
- jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +109 -71
- jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +97 -63
- jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +103 -69
- jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +271 -268
- jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +76 -64
- jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +92 -19
- jarvis/jarvis_code_agent/diff_visualizer.py +998 -0
- jarvis/jarvis_code_agent/lint.py +223 -524
- jarvis/jarvis_code_agent/rule_share_manager.py +158 -0
- jarvis/jarvis_code_agent/rules/clean_code.md +144 -0
- jarvis/jarvis_code_agent/rules/code_review.md +115 -0
- jarvis/jarvis_code_agent/rules/documentation.md +165 -0
- jarvis/jarvis_code_agent/rules/generate_rules.md +52 -0
- jarvis/jarvis_code_agent/rules/performance.md +158 -0
- jarvis/jarvis_code_agent/rules/refactoring.md +139 -0
- jarvis/jarvis_code_agent/rules/security.md +160 -0
- jarvis/jarvis_code_agent/rules/tdd.md +78 -0
- jarvis/jarvis_code_agent/test_rules/cpp_test.md +118 -0
- jarvis/jarvis_code_agent/test_rules/go_test.md +98 -0
- jarvis/jarvis_code_agent/test_rules/java_test.md +99 -0
- jarvis/jarvis_code_agent/test_rules/javascript_test.md +113 -0
- jarvis/jarvis_code_agent/test_rules/php_test.md +117 -0
- jarvis/jarvis_code_agent/test_rules/python_test.md +91 -0
- jarvis/jarvis_code_agent/test_rules/ruby_test.md +102 -0
- jarvis/jarvis_code_agent/test_rules/rust_test.md +86 -0
- jarvis/jarvis_code_agent/utils.py +36 -26
- jarvis/jarvis_code_analysis/checklists/loader.py +21 -21
- jarvis/jarvis_code_analysis/code_review.py +64 -33
- jarvis/jarvis_data/config_schema.json +285 -192
- jarvis/jarvis_git_squash/main.py +8 -6
- jarvis/jarvis_git_utils/git_commiter.py +53 -76
- jarvis/jarvis_mcp/__init__.py +5 -2
- jarvis/jarvis_mcp/sse_mcp_client.py +40 -30
- jarvis/jarvis_mcp/stdio_mcp_client.py +27 -19
- jarvis/jarvis_mcp/streamable_mcp_client.py +35 -26
- jarvis/jarvis_memory_organizer/memory_organizer.py +78 -55
- jarvis/jarvis_methodology/main.py +48 -39
- jarvis/jarvis_multi_agent/__init__.py +56 -23
- jarvis/jarvis_multi_agent/main.py +15 -18
- jarvis/jarvis_platform/base.py +179 -111
- jarvis/jarvis_platform/human.py +27 -16
- jarvis/jarvis_platform/kimi.py +52 -45
- jarvis/jarvis_platform/openai.py +101 -40
- jarvis/jarvis_platform/registry.py +51 -33
- jarvis/jarvis_platform/tongyi.py +68 -38
- jarvis/jarvis_platform/yuanbao.py +59 -43
- jarvis/jarvis_platform_manager/main.py +68 -76
- jarvis/jarvis_platform_manager/service.py +24 -14
- jarvis/jarvis_rag/README_CONFIG.md +314 -0
- jarvis/jarvis_rag/README_DYNAMIC_LOADING.md +311 -0
- jarvis/jarvis_rag/README_ONLINE_MODELS.md +230 -0
- jarvis/jarvis_rag/__init__.py +57 -4
- jarvis/jarvis_rag/cache.py +3 -1
- jarvis/jarvis_rag/cli.py +48 -68
- jarvis/jarvis_rag/embedding_interface.py +39 -0
- jarvis/jarvis_rag/embedding_manager.py +7 -230
- jarvis/jarvis_rag/embeddings/__init__.py +41 -0
- jarvis/jarvis_rag/embeddings/base.py +114 -0
- jarvis/jarvis_rag/embeddings/cohere.py +66 -0
- jarvis/jarvis_rag/embeddings/edgefn.py +117 -0
- jarvis/jarvis_rag/embeddings/local.py +260 -0
- jarvis/jarvis_rag/embeddings/openai.py +62 -0
- jarvis/jarvis_rag/embeddings/registry.py +293 -0
- jarvis/jarvis_rag/llm_interface.py +8 -6
- jarvis/jarvis_rag/query_rewriter.py +8 -9
- jarvis/jarvis_rag/rag_pipeline.py +61 -52
- jarvis/jarvis_rag/reranker.py +7 -75
- jarvis/jarvis_rag/reranker_interface.py +32 -0
- jarvis/jarvis_rag/rerankers/__init__.py +41 -0
- jarvis/jarvis_rag/rerankers/base.py +109 -0
- jarvis/jarvis_rag/rerankers/cohere.py +67 -0
- jarvis/jarvis_rag/rerankers/edgefn.py +140 -0
- jarvis/jarvis_rag/rerankers/jina.py +79 -0
- jarvis/jarvis_rag/rerankers/local.py +89 -0
- jarvis/jarvis_rag/rerankers/registry.py +293 -0
- jarvis/jarvis_rag/retriever.py +58 -43
- jarvis/jarvis_sec/__init__.py +66 -141
- jarvis/jarvis_sec/agents.py +21 -17
- jarvis/jarvis_sec/analysis.py +80 -33
- jarvis/jarvis_sec/checkers/__init__.py +7 -13
- jarvis/jarvis_sec/checkers/c_checker.py +356 -164
- jarvis/jarvis_sec/checkers/rust_checker.py +47 -29
- jarvis/jarvis_sec/cli.py +43 -21
- jarvis/jarvis_sec/clustering.py +430 -272
- jarvis/jarvis_sec/file_manager.py +99 -55
- jarvis/jarvis_sec/parsers.py +9 -6
- jarvis/jarvis_sec/prompts.py +4 -3
- jarvis/jarvis_sec/report.py +44 -22
- jarvis/jarvis_sec/review.py +180 -107
- jarvis/jarvis_sec/status.py +50 -41
- jarvis/jarvis_sec/types.py +3 -0
- jarvis/jarvis_sec/utils.py +160 -83
- jarvis/jarvis_sec/verification.py +411 -181
- jarvis/jarvis_sec/workflow.py +132 -21
- jarvis/jarvis_smart_shell/main.py +28 -41
- jarvis/jarvis_stats/cli.py +14 -12
- jarvis/jarvis_stats/stats.py +28 -19
- jarvis/jarvis_stats/storage.py +14 -8
- jarvis/jarvis_stats/visualizer.py +12 -7
- jarvis/jarvis_tools/base.py +5 -2
- jarvis/jarvis_tools/clear_memory.py +13 -9
- jarvis/jarvis_tools/cli/main.py +23 -18
- jarvis/jarvis_tools/edit_file.py +572 -873
- jarvis/jarvis_tools/execute_script.py +10 -7
- jarvis/jarvis_tools/file_analyzer.py +7 -8
- jarvis/jarvis_tools/meta_agent.py +287 -0
- jarvis/jarvis_tools/methodology.py +5 -3
- jarvis/jarvis_tools/read_code.py +305 -1438
- jarvis/jarvis_tools/read_symbols.py +50 -17
- jarvis/jarvis_tools/read_webpage.py +19 -18
- jarvis/jarvis_tools/registry.py +435 -156
- jarvis/jarvis_tools/retrieve_memory.py +16 -11
- jarvis/jarvis_tools/save_memory.py +8 -6
- jarvis/jarvis_tools/search_web.py +31 -31
- jarvis/jarvis_tools/sub_agent.py +32 -28
- jarvis/jarvis_tools/sub_code_agent.py +44 -60
- jarvis/jarvis_tools/task_list_manager.py +1811 -0
- jarvis/jarvis_tools/virtual_tty.py +29 -19
- jarvis/jarvis_utils/__init__.py +4 -0
- jarvis/jarvis_utils/builtin_replace_map.py +2 -1
- jarvis/jarvis_utils/clipboard.py +9 -8
- jarvis/jarvis_utils/collections.py +331 -0
- jarvis/jarvis_utils/config.py +699 -194
- jarvis/jarvis_utils/dialogue_recorder.py +294 -0
- jarvis/jarvis_utils/embedding.py +6 -3
- jarvis/jarvis_utils/file_processors.py +7 -1
- jarvis/jarvis_utils/fzf.py +9 -3
- jarvis/jarvis_utils/git_utils.py +71 -42
- jarvis/jarvis_utils/globals.py +116 -32
- jarvis/jarvis_utils/http.py +6 -2
- jarvis/jarvis_utils/input.py +318 -83
- jarvis/jarvis_utils/jsonnet_compat.py +119 -104
- jarvis/jarvis_utils/methodology.py +37 -28
- jarvis/jarvis_utils/output.py +201 -44
- jarvis/jarvis_utils/utils.py +986 -628
- {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/METADATA +49 -33
- jarvis_ai_assistant-1.0.2.dist-info/RECORD +304 -0
- jarvis/jarvis_code_agent/code_analyzer/structured_code.py +0 -556
- jarvis/jarvis_tools/generate_new_tool.py +0 -205
- jarvis/jarvis_tools/lsp_client.py +0 -1552
- jarvis/jarvis_tools/rewrite_file.py +0 -105
- jarvis_ai_assistant-0.7.16.dist-info/RECORD +0 -218
- {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/top_level.txt +0 -0
jarvis/jarvis_sec/utils.py
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
"""工具函数模块"""
|
|
3
3
|
|
|
4
|
-
from typing import Dict, List, Optional
|
|
5
|
-
from pathlib import Path
|
|
6
4
|
import json
|
|
7
|
-
import
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Dict
|
|
7
|
+
from typing import List
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
from jarvis.jarvis_utils.output import PrettyOutput
|
|
8
11
|
|
|
9
12
|
from jarvis.jarvis_sec.workflow import direct_scan
|
|
10
13
|
|
|
@@ -16,15 +19,26 @@ def git_restore_if_dirty(repo_root: str) -> int:
|
|
|
16
19
|
"""
|
|
17
20
|
try:
|
|
18
21
|
import subprocess as _sub
|
|
22
|
+
|
|
19
23
|
root = Path(repo_root)
|
|
20
24
|
if not (root / ".git").exists():
|
|
21
25
|
return 0
|
|
22
|
-
proc = _sub.run(
|
|
26
|
+
proc = _sub.run(
|
|
27
|
+
["git", "status", "--porcelain"],
|
|
28
|
+
cwd=str(root),
|
|
29
|
+
capture_output=True,
|
|
30
|
+
text=True,
|
|
31
|
+
)
|
|
23
32
|
if proc.returncode != 0:
|
|
24
33
|
return 0
|
|
25
34
|
lines = [line for line in proc.stdout.splitlines() if line.strip()]
|
|
26
35
|
if lines:
|
|
27
|
-
_sub.run(
|
|
36
|
+
_sub.run(
|
|
37
|
+
["git", "checkout", "--", "."],
|
|
38
|
+
cwd=str(root),
|
|
39
|
+
capture_output=True,
|
|
40
|
+
text=True,
|
|
41
|
+
)
|
|
28
42
|
return len(lines)
|
|
29
43
|
except Exception:
|
|
30
44
|
pass
|
|
@@ -47,17 +61,17 @@ def initialize_analysis_context(
|
|
|
47
61
|
) -> tuple:
|
|
48
62
|
"""
|
|
49
63
|
初始化分析上下文,包括状态管理、进度文件、目录等。
|
|
50
|
-
|
|
64
|
+
|
|
51
65
|
返回: (sec_dir, progress_path, _progress_append)
|
|
52
66
|
"""
|
|
53
67
|
# 获取 .jarvis/sec 目录
|
|
54
68
|
sec_dir = get_sec_dir(entry_path)
|
|
55
69
|
progress_path = None # 不再使用 progress.jsonl
|
|
56
|
-
|
|
70
|
+
|
|
57
71
|
# 进度追加函数(空函数,不再记录)
|
|
58
72
|
def _progress_append(rec: Dict) -> None:
|
|
59
73
|
pass # 不再记录进度日志
|
|
60
|
-
|
|
74
|
+
|
|
61
75
|
return sec_dir, progress_path, _progress_append
|
|
62
76
|
|
|
63
77
|
|
|
@@ -71,26 +85,33 @@ def load_or_run_heuristic_scan(
|
|
|
71
85
|
) -> tuple[List[Dict], Dict]:
|
|
72
86
|
"""
|
|
73
87
|
加载或运行启发式扫描。
|
|
74
|
-
|
|
88
|
+
|
|
75
89
|
优先从新的 candidates.jsonl 文件加载,如果不存在则回退到旧的 heuristic_issues.jsonl。
|
|
76
|
-
|
|
90
|
+
|
|
77
91
|
返回: (candidates, summary)
|
|
78
92
|
"""
|
|
79
93
|
candidates: List[Dict] = []
|
|
80
94
|
summary: Dict = {}
|
|
81
|
-
|
|
95
|
+
|
|
82
96
|
# 优先使用新的 candidates.jsonl 文件
|
|
83
|
-
from jarvis.jarvis_sec.file_manager import
|
|
97
|
+
from jarvis.jarvis_sec.file_manager import get_candidates_file
|
|
98
|
+
from jarvis.jarvis_sec.file_manager import load_candidates
|
|
99
|
+
|
|
84
100
|
candidates = load_candidates(sec_dir)
|
|
85
|
-
|
|
101
|
+
|
|
86
102
|
if candidates:
|
|
87
103
|
try:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
104
|
+
PrettyOutput.auto_print(
|
|
105
|
+
f"✨ [jarvis-sec] 从 {get_candidates_file(sec_dir)} 恢复启发式扫描",
|
|
106
|
+
timestamp=True,
|
|
107
|
+
)
|
|
108
|
+
_progress_append(
|
|
109
|
+
{
|
|
110
|
+
"event": "pre_scan_resumed",
|
|
111
|
+
"path": str(get_candidates_file(sec_dir)),
|
|
112
|
+
"issues_found": len(candidates),
|
|
113
|
+
}
|
|
114
|
+
)
|
|
94
115
|
except Exception:
|
|
95
116
|
pass
|
|
96
117
|
else:
|
|
@@ -98,22 +119,32 @@ def load_or_run_heuristic_scan(
|
|
|
98
119
|
_heuristic_path = sec_dir / "heuristic_issues.jsonl"
|
|
99
120
|
if _heuristic_path.exists():
|
|
100
121
|
try:
|
|
101
|
-
|
|
122
|
+
PrettyOutput.auto_print(
|
|
123
|
+
f"✨ [jarvis-sec] 从 {_heuristic_path} 恢复启发式扫描(旧格式)",
|
|
124
|
+
timestamp=True,
|
|
125
|
+
)
|
|
102
126
|
with _heuristic_path.open("r", encoding="utf-8") as f:
|
|
103
127
|
for line in f:
|
|
104
128
|
if line.strip():
|
|
105
129
|
candidates.append(json.loads(line))
|
|
106
|
-
_progress_append(
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
130
|
+
_progress_append(
|
|
131
|
+
{
|
|
132
|
+
"event": "pre_scan_resumed",
|
|
133
|
+
"path": str(_heuristic_path),
|
|
134
|
+
"issues_found": len(candidates),
|
|
135
|
+
}
|
|
136
|
+
)
|
|
111
137
|
except Exception as e:
|
|
112
|
-
|
|
138
|
+
PrettyOutput.auto_print(
|
|
139
|
+
f"⚠️ [jarvis-sec] 恢复启发式扫描失败,执行完整扫描: {e}",
|
|
140
|
+
timestamp=True,
|
|
141
|
+
)
|
|
113
142
|
candidates = [] # 重置以便执行完整扫描
|
|
114
|
-
|
|
143
|
+
|
|
115
144
|
if not candidates:
|
|
116
|
-
_progress_append(
|
|
145
|
+
_progress_append(
|
|
146
|
+
{"event": "pre_scan_start", "entry_path": entry_path, "languages": langs}
|
|
147
|
+
)
|
|
117
148
|
status_mgr.update_pre_scan(message="开始启发式扫描...")
|
|
118
149
|
pre_scan = direct_scan(entry_path, languages=langs, exclude_dirs=exclude_dirs)
|
|
119
150
|
candidates = pre_scan.get("issues", [])
|
|
@@ -123,36 +154,43 @@ def load_or_run_heuristic_scan(
|
|
|
123
154
|
current_files=scanned_files,
|
|
124
155
|
total_files=scanned_files,
|
|
125
156
|
issues_found=len(candidates),
|
|
126
|
-
message=f"启发式扫描完成,发现 {len(candidates)} 个候选问题"
|
|
157
|
+
message=f"启发式扫描完成,发现 {len(candidates)} 个候选问题",
|
|
158
|
+
)
|
|
159
|
+
_progress_append(
|
|
160
|
+
{
|
|
161
|
+
"event": "pre_scan_done",
|
|
162
|
+
"entry_path": entry_path,
|
|
163
|
+
"languages": langs,
|
|
164
|
+
"scanned_files": scanned_files,
|
|
165
|
+
"issues_found": len(candidates),
|
|
166
|
+
}
|
|
127
167
|
)
|
|
128
|
-
_progress_append({
|
|
129
|
-
"event": "pre_scan_done",
|
|
130
|
-
"entry_path": entry_path,
|
|
131
|
-
"languages": langs,
|
|
132
|
-
"scanned_files": scanned_files,
|
|
133
|
-
"issues_found": len(candidates)
|
|
134
|
-
})
|
|
135
168
|
# 持久化
|
|
136
169
|
try:
|
|
137
170
|
_heuristic_path.parent.mkdir(parents=True, exist_ok=True)
|
|
138
171
|
with _heuristic_path.open("w", encoding="utf-8") as f:
|
|
139
172
|
for item in candidates:
|
|
140
173
|
f.write(json.dumps(item, ensure_ascii=False) + "\n")
|
|
141
|
-
_progress_append(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
174
|
+
_progress_append(
|
|
175
|
+
{
|
|
176
|
+
"event": "heuristic_report_written",
|
|
177
|
+
"path": str(_heuristic_path),
|
|
178
|
+
"issues_count": len(candidates),
|
|
179
|
+
}
|
|
180
|
+
)
|
|
181
|
+
PrettyOutput.auto_print(
|
|
182
|
+
f"✅ [jarvis-sec] 已将 {len(candidates)} 个启发式扫描问题写入 {_heuristic_path}",
|
|
183
|
+
timestamp=True,
|
|
184
|
+
)
|
|
147
185
|
except Exception:
|
|
148
186
|
pass
|
|
149
187
|
else:
|
|
150
188
|
# 从断点恢复启发式扫描结果
|
|
151
189
|
status_mgr.update_pre_scan(
|
|
152
190
|
issues_found=len(candidates),
|
|
153
|
-
message=f"从断点恢复,已发现 {len(candidates)} 个候选问题"
|
|
191
|
+
message=f"从断点恢复,已发现 {len(candidates)} 个候选问题",
|
|
154
192
|
)
|
|
155
|
-
|
|
193
|
+
|
|
156
194
|
return candidates, summary
|
|
157
195
|
|
|
158
196
|
|
|
@@ -182,14 +220,17 @@ def compact_candidate(it: Dict) -> Dict:
|
|
|
182
220
|
def prepare_candidates(candidates: List[Dict]) -> List[Dict]:
|
|
183
221
|
"""
|
|
184
222
|
将候选问题精简为子任务清单,控制上下文长度,并分配全局唯一ID。
|
|
185
|
-
|
|
223
|
+
|
|
186
224
|
返回: compact_candidates (已分配gid的候选列表)
|
|
187
225
|
"""
|
|
188
226
|
compact_candidates = [compact_candidate(it) for it in candidates]
|
|
189
|
-
|
|
227
|
+
|
|
190
228
|
# 检查是否所有候选都已经有gid(从heuristic_issues.jsonl恢复时)
|
|
191
|
-
all_have_gid = all(
|
|
192
|
-
|
|
229
|
+
all_have_gid = all(
|
|
230
|
+
"gid" in it and isinstance(it.get("gid"), int) and it.get("gid", 0) >= 1
|
|
231
|
+
for it in compact_candidates
|
|
232
|
+
)
|
|
233
|
+
|
|
193
234
|
if not all_have_gid:
|
|
194
235
|
# 如果有候选没有gid,需要分配
|
|
195
236
|
# 优先保留已有的gid,为没有gid的候选分配新的gid
|
|
@@ -201,11 +242,15 @@ def prepare_candidates(candidates: List[Dict]) -> List[Dict]:
|
|
|
201
242
|
existing_gids.add(gid_val)
|
|
202
243
|
except Exception:
|
|
203
244
|
pass
|
|
204
|
-
|
|
245
|
+
|
|
205
246
|
# 为没有gid的候选分配新的gid
|
|
206
247
|
next_gid = 1
|
|
207
248
|
for it in compact_candidates:
|
|
208
|
-
if
|
|
249
|
+
if (
|
|
250
|
+
"gid" not in it
|
|
251
|
+
or not isinstance(it.get("gid"), int)
|
|
252
|
+
or it.get("gid", 0) < 1
|
|
253
|
+
):
|
|
209
254
|
# 找到一个未使用的gid
|
|
210
255
|
while next_gid in existing_gids:
|
|
211
256
|
next_gid += 1
|
|
@@ -215,13 +260,14 @@ def prepare_candidates(candidates: List[Dict]) -> List[Dict]:
|
|
|
215
260
|
next_gid += 1
|
|
216
261
|
except Exception:
|
|
217
262
|
pass
|
|
218
|
-
|
|
263
|
+
|
|
219
264
|
return compact_candidates
|
|
220
265
|
|
|
221
266
|
|
|
222
267
|
def group_candidates_by_file(candidates: List[Dict]) -> Dict[str, List[Dict]]:
|
|
223
268
|
"""按文件分组候选问题"""
|
|
224
269
|
from collections import defaultdict
|
|
270
|
+
|
|
225
271
|
groups: Dict[str, List[Dict]] = defaultdict(list)
|
|
226
272
|
for it in candidates:
|
|
227
273
|
groups[str(it.get("file") or "")].append(it)
|
|
@@ -230,12 +276,13 @@ def group_candidates_by_file(candidates: List[Dict]) -> Dict[str, List[Dict]]:
|
|
|
230
276
|
|
|
231
277
|
def create_report_writer(sec_dir: Path, report_file: Optional[str]):
|
|
232
278
|
"""创建报告写入函数"""
|
|
233
|
-
from jarvis.jarvis_sec.file_manager import
|
|
234
|
-
|
|
279
|
+
from jarvis.jarvis_sec.file_manager import load_clusters
|
|
280
|
+
from jarvis.jarvis_sec.file_manager import save_analysis_result
|
|
281
|
+
|
|
235
282
|
def _append_report(items, source: str, task_id: str, cand: Dict):
|
|
236
283
|
"""
|
|
237
284
|
将当前子任务的检测结果追加写入 analysis.jsonl 文件。
|
|
238
|
-
|
|
285
|
+
|
|
239
286
|
参数:
|
|
240
287
|
- items: 验证通过的问题列表(has_risk: true)
|
|
241
288
|
- source: 来源("analysis_only" 或 "verified")
|
|
@@ -244,26 +291,31 @@ def create_report_writer(sec_dir: Path, report_file: Optional[str]):
|
|
|
244
291
|
"""
|
|
245
292
|
if not items:
|
|
246
293
|
return
|
|
247
|
-
|
|
294
|
+
|
|
248
295
|
try:
|
|
249
296
|
# 从批次中提取信息
|
|
250
297
|
batch = cand.get("batch", False)
|
|
251
298
|
candidates = cand.get("candidates", [])
|
|
252
|
-
|
|
299
|
+
|
|
253
300
|
if not batch or not candidates:
|
|
254
301
|
# 如果没有批次信息,回退到旧格式(向后兼容)
|
|
255
|
-
path =
|
|
302
|
+
path = (
|
|
303
|
+
Path(report_file) if report_file else sec_dir / "agent_issues.jsonl"
|
|
304
|
+
)
|
|
256
305
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
257
306
|
with path.open("a", encoding="utf-8") as f:
|
|
258
307
|
for item in items:
|
|
259
308
|
line = json.dumps(item, ensure_ascii=False)
|
|
260
309
|
f.write(line + "\n")
|
|
261
310
|
try:
|
|
262
|
-
|
|
311
|
+
PrettyOutput.auto_print(
|
|
312
|
+
f"✅ [jarvis-sec] 已将 {len(items)} 个问题写入 {path}(旧格式)",
|
|
313
|
+
timestamp=True,
|
|
314
|
+
)
|
|
263
315
|
except Exception:
|
|
264
316
|
pass
|
|
265
317
|
return
|
|
266
|
-
|
|
318
|
+
|
|
267
319
|
# 从批次中提取 file 和 gids
|
|
268
320
|
batch_file = candidates[0].get("file") if candidates else ""
|
|
269
321
|
batch_gids = []
|
|
@@ -274,25 +326,25 @@ def create_report_writer(sec_dir: Path, report_file: Optional[str]):
|
|
|
274
326
|
batch_gids.append(gid)
|
|
275
327
|
except Exception:
|
|
276
328
|
pass
|
|
277
|
-
|
|
329
|
+
|
|
278
330
|
# 从 clusters.jsonl 中查找对应的 cluster_id
|
|
279
331
|
clusters = load_clusters(sec_dir)
|
|
280
332
|
cluster_id = None
|
|
281
333
|
batch_index = None
|
|
282
334
|
cluster_index = None
|
|
283
|
-
|
|
335
|
+
|
|
284
336
|
# 尝试从 task_id 中提取 batch_index(格式:JARVIS-SEC-Batch-1)
|
|
285
337
|
try:
|
|
286
338
|
if "Batch-" in task_id:
|
|
287
339
|
batch_index = int(task_id.split("Batch-")[1])
|
|
288
340
|
except Exception:
|
|
289
341
|
pass
|
|
290
|
-
|
|
342
|
+
|
|
291
343
|
# 查找匹配的聚类(通过 file 和 gids)
|
|
292
344
|
for cluster in clusters:
|
|
293
345
|
cluster_file = str(cluster.get("file", ""))
|
|
294
346
|
cluster_gids = cluster.get("gids", [])
|
|
295
|
-
|
|
347
|
+
|
|
296
348
|
if cluster_file == batch_file and set(cluster_gids) == set(batch_gids):
|
|
297
349
|
cluster_id = cluster.get("cluster_id", "")
|
|
298
350
|
if not cluster_id:
|
|
@@ -301,18 +353,18 @@ def create_report_writer(sec_dir: Path, report_file: Optional[str]):
|
|
|
301
353
|
batch_index = cluster.get("batch_index", batch_index or 0)
|
|
302
354
|
cluster_index = cluster.get("cluster_index", 0)
|
|
303
355
|
break
|
|
304
|
-
|
|
356
|
+
|
|
305
357
|
# 如果找不到匹配的聚类,生成一个临时的 cluster_id
|
|
306
358
|
if not cluster_id:
|
|
307
359
|
cluster_id = f"{batch_file}|{batch_index or 0}|0"
|
|
308
360
|
batch_index = batch_index or 0
|
|
309
361
|
cluster_index = 0
|
|
310
|
-
|
|
362
|
+
|
|
311
363
|
# 分离验证为问题的gid和误报的gid
|
|
312
364
|
verified_gids = []
|
|
313
365
|
false_positive_gids = []
|
|
314
366
|
issues = []
|
|
315
|
-
|
|
367
|
+
|
|
316
368
|
# 从 items 中提取已验证的问题
|
|
317
369
|
for item in items:
|
|
318
370
|
try:
|
|
@@ -326,17 +378,21 @@ def create_report_writer(sec_dir: Path, report_file: Optional[str]):
|
|
|
326
378
|
false_positive_gids.append(gid)
|
|
327
379
|
except Exception:
|
|
328
380
|
pass
|
|
329
|
-
|
|
381
|
+
|
|
330
382
|
# 从 candidates 中提取所有未在 items 中的 gid(这些可能是误报)
|
|
331
383
|
for c in candidates:
|
|
332
384
|
try:
|
|
333
385
|
gid = int(c.get("gid", 0))
|
|
334
|
-
if
|
|
386
|
+
if (
|
|
387
|
+
gid >= 1
|
|
388
|
+
and gid not in verified_gids
|
|
389
|
+
and gid not in false_positive_gids
|
|
390
|
+
):
|
|
335
391
|
# 如果这个 gid 不在已验证的问题中,可能是误报
|
|
336
392
|
false_positive_gids.append(gid)
|
|
337
393
|
except Exception:
|
|
338
394
|
pass
|
|
339
|
-
|
|
395
|
+
|
|
340
396
|
# 构建分析结果记录
|
|
341
397
|
analysis_result = {
|
|
342
398
|
"cluster_id": cluster_id,
|
|
@@ -348,27 +404,30 @@ def create_report_writer(sec_dir: Path, report_file: Optional[str]):
|
|
|
348
404
|
"false_positive_gids": false_positive_gids,
|
|
349
405
|
"issues": issues,
|
|
350
406
|
}
|
|
351
|
-
|
|
407
|
+
|
|
352
408
|
# 保存到 analysis.jsonl
|
|
353
409
|
save_analysis_result(sec_dir, analysis_result)
|
|
354
|
-
|
|
410
|
+
|
|
355
411
|
try:
|
|
356
|
-
|
|
412
|
+
PrettyOutput.auto_print(
|
|
413
|
+
f"✅ [jarvis-sec] 已将批次 {batch_index} 的分析结果写入 analysis.jsonl(问题: {len(verified_gids)}, 误报: {len(false_positive_gids)})",
|
|
414
|
+
timestamp=True,
|
|
415
|
+
)
|
|
357
416
|
except Exception:
|
|
358
417
|
pass
|
|
359
418
|
except Exception as e:
|
|
360
419
|
# 报告写入失败不影响主流程
|
|
361
420
|
try:
|
|
362
|
-
|
|
421
|
+
PrettyOutput.auto_print(f"⚠️ [jarvis-sec] 警告:保存分析结果失败: {e}")
|
|
363
422
|
except Exception:
|
|
364
423
|
pass
|
|
365
|
-
|
|
424
|
+
|
|
366
425
|
return _append_report
|
|
367
426
|
|
|
368
427
|
|
|
369
428
|
def sig_of(c: Dict) -> str:
|
|
370
429
|
"""生成候选问题的签名"""
|
|
371
|
-
return f"{c.get('language','')}|{c.get('file','')}|{c.get('line','')}|{c.get('pattern','')}"
|
|
430
|
+
return f"{c.get('language', '')}|{c.get('file', '')}|{c.get('line', '')}|{c.get('pattern', '')}"
|
|
372
431
|
|
|
373
432
|
|
|
374
433
|
def load_processed_gids_from_issues(sec_dir: Path) -> set:
|
|
@@ -391,7 +450,10 @@ def load_processed_gids_from_issues(sec_dir: Path) -> set:
|
|
|
391
450
|
pass
|
|
392
451
|
if processed_gids:
|
|
393
452
|
try:
|
|
394
|
-
|
|
453
|
+
PrettyOutput.auto_print(
|
|
454
|
+
f"✨ [jarvis-sec] 断点恢复:从 agent_issues.jsonl 读取到 {len(processed_gids)} 个已处理的 gid",
|
|
455
|
+
timestamp=True,
|
|
456
|
+
)
|
|
395
457
|
except Exception:
|
|
396
458
|
pass
|
|
397
459
|
except Exception:
|
|
@@ -402,6 +464,7 @@ def load_processed_gids_from_issues(sec_dir: Path) -> set:
|
|
|
402
464
|
def count_issues_from_file(sec_dir: Path) -> int:
|
|
403
465
|
"""从 analysis.jsonl 读取问题数量"""
|
|
404
466
|
from jarvis.jarvis_sec.file_manager import get_verified_issue_gids
|
|
467
|
+
|
|
405
468
|
verified_gids = get_verified_issue_gids(sec_dir)
|
|
406
469
|
return len(verified_gids)
|
|
407
470
|
|
|
@@ -423,7 +486,10 @@ def count_issues_from_file_old(sec_dir: Path) -> int:
|
|
|
423
486
|
gid = item.get("gid", 0)
|
|
424
487
|
if gid >= 1 and gid not in saved_gids:
|
|
425
488
|
# 只统计验证通过的告警(has_risk: true 且有 verification_notes)
|
|
426
|
-
if
|
|
489
|
+
if (
|
|
490
|
+
item.get("has_risk") is True
|
|
491
|
+
and "verification_notes" in item
|
|
492
|
+
):
|
|
427
493
|
count += 1
|
|
428
494
|
saved_gids.add(gid)
|
|
429
495
|
except Exception:
|
|
@@ -450,26 +516,38 @@ def load_all_issues_from_file(sec_dir: Path) -> List[Dict]:
|
|
|
450
516
|
gid = item.get("gid", 0)
|
|
451
517
|
if gid >= 1 and gid not in saved_gids_from_file:
|
|
452
518
|
# 只保留验证通过的告警(has_risk: true 且有 verification_notes)
|
|
453
|
-
if
|
|
519
|
+
if (
|
|
520
|
+
item.get("has_risk") is True
|
|
521
|
+
and "verification_notes" in item
|
|
522
|
+
):
|
|
454
523
|
all_issues.append(item)
|
|
455
524
|
saved_gids_from_file.add(gid)
|
|
456
525
|
except Exception:
|
|
457
526
|
pass
|
|
458
|
-
|
|
527
|
+
|
|
459
528
|
if all_issues:
|
|
460
529
|
try:
|
|
461
|
-
|
|
530
|
+
PrettyOutput.auto_print(
|
|
531
|
+
f"✨ [jarvis-sec] 从 agent_issues.jsonl 加载了 {len(all_issues)} 个已保存的告警",
|
|
532
|
+
timestamp=True,
|
|
533
|
+
)
|
|
462
534
|
except Exception:
|
|
463
535
|
pass
|
|
464
536
|
else:
|
|
465
537
|
try:
|
|
466
|
-
|
|
538
|
+
PrettyOutput.auto_print(
|
|
539
|
+
"✨ [jarvis-sec] agent_issues.jsonl 不存在,当前运行未发现任何问题",
|
|
540
|
+
timestamp=True,
|
|
541
|
+
)
|
|
467
542
|
except Exception:
|
|
468
543
|
pass
|
|
469
544
|
except Exception as e:
|
|
470
545
|
# 加载失败不影响主流程
|
|
471
546
|
try:
|
|
472
|
-
|
|
547
|
+
PrettyOutput.auto_print(
|
|
548
|
+
f"⚠️ [jarvis-sec] 警告:从 agent_issues.jsonl 加载告警失败: {e}",
|
|
549
|
+
timestamp=True,
|
|
550
|
+
)
|
|
473
551
|
except Exception:
|
|
474
552
|
pass
|
|
475
553
|
return all_issues
|
|
@@ -496,4 +574,3 @@ def load_processed_gids_from_agent_issues(sec_dir: Path) -> set:
|
|
|
496
574
|
except Exception:
|
|
497
575
|
pass
|
|
498
576
|
return processed_gids
|
|
499
|
-
|