aiptx 2.0.7__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.
- aipt_v2/__init__.py +110 -0
- aipt_v2/__main__.py +24 -0
- aipt_v2/agents/AIPTxAgent/__init__.py +10 -0
- aipt_v2/agents/AIPTxAgent/aiptx_agent.py +211 -0
- aipt_v2/agents/__init__.py +46 -0
- aipt_v2/agents/base.py +520 -0
- aipt_v2/agents/exploit_agent.py +688 -0
- aipt_v2/agents/ptt.py +406 -0
- aipt_v2/agents/state.py +168 -0
- aipt_v2/app.py +957 -0
- aipt_v2/browser/__init__.py +31 -0
- aipt_v2/browser/automation.py +458 -0
- aipt_v2/browser/crawler.py +453 -0
- aipt_v2/cli.py +2933 -0
- aipt_v2/compliance/__init__.py +71 -0
- aipt_v2/compliance/compliance_report.py +449 -0
- aipt_v2/compliance/framework_mapper.py +424 -0
- aipt_v2/compliance/nist_mapping.py +345 -0
- aipt_v2/compliance/owasp_mapping.py +330 -0
- aipt_v2/compliance/pci_mapping.py +297 -0
- aipt_v2/config.py +341 -0
- aipt_v2/core/__init__.py +43 -0
- aipt_v2/core/agent.py +630 -0
- aipt_v2/core/llm.py +395 -0
- aipt_v2/core/memory.py +305 -0
- aipt_v2/core/ptt.py +329 -0
- aipt_v2/database/__init__.py +14 -0
- aipt_v2/database/models.py +232 -0
- aipt_v2/database/repository.py +384 -0
- aipt_v2/docker/__init__.py +23 -0
- aipt_v2/docker/builder.py +260 -0
- aipt_v2/docker/manager.py +222 -0
- aipt_v2/docker/sandbox.py +371 -0
- aipt_v2/evasion/__init__.py +58 -0
- aipt_v2/evasion/request_obfuscator.py +272 -0
- aipt_v2/evasion/tls_fingerprint.py +285 -0
- aipt_v2/evasion/ua_rotator.py +301 -0
- aipt_v2/evasion/waf_bypass.py +439 -0
- aipt_v2/execution/__init__.py +23 -0
- aipt_v2/execution/executor.py +302 -0
- aipt_v2/execution/parser.py +544 -0
- aipt_v2/execution/terminal.py +337 -0
- aipt_v2/health.py +437 -0
- aipt_v2/intelligence/__init__.py +194 -0
- aipt_v2/intelligence/adaptation.py +474 -0
- aipt_v2/intelligence/auth.py +520 -0
- aipt_v2/intelligence/chaining.py +775 -0
- aipt_v2/intelligence/correlation.py +536 -0
- aipt_v2/intelligence/cve_aipt.py +334 -0
- aipt_v2/intelligence/cve_info.py +1111 -0
- aipt_v2/intelligence/knowledge_graph.py +590 -0
- aipt_v2/intelligence/learning.py +626 -0
- aipt_v2/intelligence/llm_analyzer.py +502 -0
- aipt_v2/intelligence/llm_tool_selector.py +518 -0
- aipt_v2/intelligence/payload_generator.py +562 -0
- aipt_v2/intelligence/rag.py +239 -0
- aipt_v2/intelligence/scope.py +442 -0
- aipt_v2/intelligence/searchers/__init__.py +5 -0
- aipt_v2/intelligence/searchers/exploitdb_searcher.py +523 -0
- aipt_v2/intelligence/searchers/github_searcher.py +467 -0
- aipt_v2/intelligence/searchers/google_searcher.py +281 -0
- aipt_v2/intelligence/tools.json +443 -0
- aipt_v2/intelligence/triage.py +670 -0
- aipt_v2/interactive_shell.py +559 -0
- aipt_v2/interface/__init__.py +5 -0
- aipt_v2/interface/cli.py +230 -0
- aipt_v2/interface/main.py +501 -0
- aipt_v2/interface/tui.py +1276 -0
- aipt_v2/interface/utils.py +583 -0
- aipt_v2/llm/__init__.py +39 -0
- aipt_v2/llm/config.py +26 -0
- aipt_v2/llm/llm.py +514 -0
- aipt_v2/llm/memory.py +214 -0
- aipt_v2/llm/request_queue.py +89 -0
- aipt_v2/llm/utils.py +89 -0
- aipt_v2/local_tool_installer.py +1467 -0
- aipt_v2/models/__init__.py +15 -0
- aipt_v2/models/findings.py +295 -0
- aipt_v2/models/phase_result.py +224 -0
- aipt_v2/models/scan_config.py +207 -0
- aipt_v2/monitoring/grafana/dashboards/aipt-dashboard.json +355 -0
- aipt_v2/monitoring/grafana/dashboards/default.yml +17 -0
- aipt_v2/monitoring/grafana/datasources/prometheus.yml +17 -0
- aipt_v2/monitoring/prometheus.yml +60 -0
- aipt_v2/orchestration/__init__.py +52 -0
- aipt_v2/orchestration/pipeline.py +398 -0
- aipt_v2/orchestration/progress.py +300 -0
- aipt_v2/orchestration/scheduler.py +296 -0
- aipt_v2/orchestrator.py +2427 -0
- aipt_v2/payloads/__init__.py +27 -0
- aipt_v2/payloads/cmdi.py +150 -0
- aipt_v2/payloads/sqli.py +263 -0
- aipt_v2/payloads/ssrf.py +204 -0
- aipt_v2/payloads/templates.py +222 -0
- aipt_v2/payloads/traversal.py +166 -0
- aipt_v2/payloads/xss.py +204 -0
- aipt_v2/prompts/__init__.py +60 -0
- aipt_v2/proxy/__init__.py +29 -0
- aipt_v2/proxy/history.py +352 -0
- aipt_v2/proxy/interceptor.py +452 -0
- aipt_v2/recon/__init__.py +44 -0
- aipt_v2/recon/dns.py +241 -0
- aipt_v2/recon/osint.py +367 -0
- aipt_v2/recon/subdomain.py +372 -0
- aipt_v2/recon/tech_detect.py +311 -0
- aipt_v2/reports/__init__.py +17 -0
- aipt_v2/reports/generator.py +313 -0
- aipt_v2/reports/html_report.py +378 -0
- aipt_v2/runtime/__init__.py +53 -0
- aipt_v2/runtime/base.py +30 -0
- aipt_v2/runtime/docker.py +401 -0
- aipt_v2/runtime/local.py +346 -0
- aipt_v2/runtime/tool_server.py +205 -0
- aipt_v2/runtime/vps.py +830 -0
- aipt_v2/scanners/__init__.py +28 -0
- aipt_v2/scanners/base.py +273 -0
- aipt_v2/scanners/nikto.py +244 -0
- aipt_v2/scanners/nmap.py +402 -0
- aipt_v2/scanners/nuclei.py +273 -0
- aipt_v2/scanners/web.py +454 -0
- aipt_v2/scripts/security_audit.py +366 -0
- aipt_v2/setup_wizard.py +941 -0
- aipt_v2/skills/__init__.py +80 -0
- aipt_v2/skills/agents/__init__.py +14 -0
- aipt_v2/skills/agents/api_tester.py +706 -0
- aipt_v2/skills/agents/base.py +477 -0
- aipt_v2/skills/agents/code_review.py +459 -0
- aipt_v2/skills/agents/security_agent.py +336 -0
- aipt_v2/skills/agents/web_pentest.py +818 -0
- aipt_v2/skills/prompts/__init__.py +647 -0
- aipt_v2/system_detector.py +539 -0
- aipt_v2/telemetry/__init__.py +7 -0
- aipt_v2/telemetry/tracer.py +347 -0
- aipt_v2/terminal/__init__.py +28 -0
- aipt_v2/terminal/executor.py +400 -0
- aipt_v2/terminal/sandbox.py +350 -0
- aipt_v2/tools/__init__.py +44 -0
- aipt_v2/tools/active_directory/__init__.py +78 -0
- aipt_v2/tools/active_directory/ad_config.py +238 -0
- aipt_v2/tools/active_directory/bloodhound_wrapper.py +447 -0
- aipt_v2/tools/active_directory/kerberos_attacks.py +430 -0
- aipt_v2/tools/active_directory/ldap_enum.py +533 -0
- aipt_v2/tools/active_directory/smb_attacks.py +505 -0
- aipt_v2/tools/agents_graph/__init__.py +19 -0
- aipt_v2/tools/agents_graph/agents_graph_actions.py +69 -0
- aipt_v2/tools/api_security/__init__.py +76 -0
- aipt_v2/tools/api_security/api_discovery.py +608 -0
- aipt_v2/tools/api_security/graphql_scanner.py +622 -0
- aipt_v2/tools/api_security/jwt_analyzer.py +577 -0
- aipt_v2/tools/api_security/openapi_fuzzer.py +761 -0
- aipt_v2/tools/browser/__init__.py +5 -0
- aipt_v2/tools/browser/browser_actions.py +238 -0
- aipt_v2/tools/browser/browser_instance.py +535 -0
- aipt_v2/tools/browser/tab_manager.py +344 -0
- aipt_v2/tools/cloud/__init__.py +70 -0
- aipt_v2/tools/cloud/cloud_config.py +273 -0
- aipt_v2/tools/cloud/cloud_scanner.py +639 -0
- aipt_v2/tools/cloud/prowler_tool.py +571 -0
- aipt_v2/tools/cloud/scoutsuite_tool.py +359 -0
- aipt_v2/tools/executor.py +307 -0
- aipt_v2/tools/parser.py +408 -0
- aipt_v2/tools/proxy/__init__.py +5 -0
- aipt_v2/tools/proxy/proxy_actions.py +103 -0
- aipt_v2/tools/proxy/proxy_manager.py +789 -0
- aipt_v2/tools/registry.py +196 -0
- aipt_v2/tools/scanners/__init__.py +343 -0
- aipt_v2/tools/scanners/acunetix_tool.py +712 -0
- aipt_v2/tools/scanners/burp_tool.py +631 -0
- aipt_v2/tools/scanners/config.py +156 -0
- aipt_v2/tools/scanners/nessus_tool.py +588 -0
- aipt_v2/tools/scanners/zap_tool.py +612 -0
- aipt_v2/tools/terminal/__init__.py +5 -0
- aipt_v2/tools/terminal/terminal_actions.py +37 -0
- aipt_v2/tools/terminal/terminal_manager.py +153 -0
- aipt_v2/tools/terminal/terminal_session.py +449 -0
- aipt_v2/tools/tool_processing.py +108 -0
- aipt_v2/utils/__init__.py +17 -0
- aipt_v2/utils/logging.py +202 -0
- aipt_v2/utils/model_manager.py +187 -0
- aipt_v2/utils/searchers/__init__.py +269 -0
- aipt_v2/verify_install.py +793 -0
- aiptx-2.0.7.dist-info/METADATA +345 -0
- aiptx-2.0.7.dist-info/RECORD +187 -0
- aiptx-2.0.7.dist-info/WHEEL +5 -0
- aiptx-2.0.7.dist-info/entry_points.txt +7 -0
- aiptx-2.0.7.dist-info/licenses/LICENSE +21 -0
- aiptx-2.0.7.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AIPT Execution Engine - Unified execution interface
|
|
3
|
+
|
|
4
|
+
Provides a unified interface for command execution with:
|
|
5
|
+
- Local terminal execution
|
|
6
|
+
- Docker sandbox execution
|
|
7
|
+
- Automatic mode selection
|
|
8
|
+
- Result parsing integration
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from typing import Optional, List, Dict, Any, Callable
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
from enum import Enum
|
|
15
|
+
import asyncio
|
|
16
|
+
|
|
17
|
+
from .terminal import Terminal, ExecutionResult
|
|
18
|
+
from .parser import OutputParser, Finding
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ExecutionMode(str, Enum):
|
|
22
|
+
"""Execution mode"""
|
|
23
|
+
LOCAL = "local" # Direct local execution
|
|
24
|
+
DOCKER = "docker" # Docker sandbox
|
|
25
|
+
AUTO = "auto" # Auto-select based on tool/risk
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class ExecutionConfig:
|
|
30
|
+
"""Configuration for execution engine"""
|
|
31
|
+
mode: ExecutionMode = ExecutionMode.AUTO
|
|
32
|
+
default_timeout: int = 300
|
|
33
|
+
max_output: int = 50000
|
|
34
|
+
parse_output: bool = True
|
|
35
|
+
sandbox_image: str = "kalilinux/kali-rolling"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ExecutionEngine:
|
|
39
|
+
"""
|
|
40
|
+
Unified execution engine for AIPT.
|
|
41
|
+
|
|
42
|
+
Provides a single interface for:
|
|
43
|
+
- Local command execution
|
|
44
|
+
- Docker sandbox execution
|
|
45
|
+
- Output parsing
|
|
46
|
+
- Result handling
|
|
47
|
+
|
|
48
|
+
Example:
|
|
49
|
+
engine = ExecutionEngine()
|
|
50
|
+
|
|
51
|
+
# Execute locally
|
|
52
|
+
result = engine.execute("nmap -F target.com")
|
|
53
|
+
|
|
54
|
+
# Execute in Docker
|
|
55
|
+
result = engine.execute("nmap -F target.com", mode=ExecutionMode.DOCKER)
|
|
56
|
+
|
|
57
|
+
# Auto-select mode based on risk
|
|
58
|
+
result = engine.execute("sqlmap -u http://target.com/page?id=1")
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
# Tools that should run in sandbox by default
|
|
62
|
+
SANDBOX_TOOLS = {
|
|
63
|
+
"metasploit", "msfconsole", "msfvenom",
|
|
64
|
+
"sqlmap", "hydra", "john", "hashcat",
|
|
65
|
+
"exploitdb", "searchsploit",
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# Tools safe to run locally
|
|
69
|
+
LOCAL_TOOLS = {
|
|
70
|
+
"nmap", "masscan", "ping", "whois", "dig", "host",
|
|
71
|
+
"curl", "wget", "httpx", "nuclei", "nikto",
|
|
72
|
+
"gobuster", "ffuf", "dirb", "subfinder",
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
def __init__(
|
|
76
|
+
self,
|
|
77
|
+
config: Optional[ExecutionConfig] = None,
|
|
78
|
+
terminal: Optional[Terminal] = None,
|
|
79
|
+
parser: Optional[OutputParser] = None,
|
|
80
|
+
):
|
|
81
|
+
self.config = config or ExecutionConfig()
|
|
82
|
+
self.terminal = terminal or Terminal(
|
|
83
|
+
default_timeout=self.config.default_timeout,
|
|
84
|
+
max_output=self.config.max_output,
|
|
85
|
+
)
|
|
86
|
+
self.parser = parser or OutputParser()
|
|
87
|
+
self._sandbox = None
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def sandbox(self):
|
|
91
|
+
"""Lazy-load Docker sandbox"""
|
|
92
|
+
if self._sandbox is None:
|
|
93
|
+
try:
|
|
94
|
+
from ..docker import DockerSandbox, SandboxConfig
|
|
95
|
+
self._sandbox = DockerSandbox(
|
|
96
|
+
SandboxConfig(image=self.config.sandbox_image)
|
|
97
|
+
)
|
|
98
|
+
except ImportError:
|
|
99
|
+
self._sandbox = False # Mark as unavailable
|
|
100
|
+
return self._sandbox if self._sandbox is not False else None
|
|
101
|
+
|
|
102
|
+
def execute(
|
|
103
|
+
self,
|
|
104
|
+
command: str,
|
|
105
|
+
mode: Optional[ExecutionMode] = None,
|
|
106
|
+
timeout: Optional[int] = None,
|
|
107
|
+
tool_name: Optional[str] = None,
|
|
108
|
+
parse: Optional[bool] = None,
|
|
109
|
+
callback: Optional[Callable[[str], None]] = None,
|
|
110
|
+
**kwargs
|
|
111
|
+
) -> Dict[str, Any]:
|
|
112
|
+
"""
|
|
113
|
+
Execute a command with automatic mode selection.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
command: Command to execute
|
|
117
|
+
mode: Execution mode (local, docker, auto)
|
|
118
|
+
timeout: Command timeout
|
|
119
|
+
tool_name: Name of the tool (for parsing)
|
|
120
|
+
parse: Whether to parse output
|
|
121
|
+
callback: Streaming callback function
|
|
122
|
+
**kwargs: Additional execution options
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Dict with:
|
|
126
|
+
- result: ExecutionResult
|
|
127
|
+
- findings: List[Finding] (if parse=True)
|
|
128
|
+
- mode: ExecutionMode used
|
|
129
|
+
"""
|
|
130
|
+
mode = mode or self.config.mode
|
|
131
|
+
timeout = timeout or self.config.default_timeout
|
|
132
|
+
parse = parse if parse is not None else self.config.parse_output
|
|
133
|
+
|
|
134
|
+
# Auto-select mode
|
|
135
|
+
if mode == ExecutionMode.AUTO:
|
|
136
|
+
mode = self._select_mode(command, tool_name)
|
|
137
|
+
|
|
138
|
+
# Execute
|
|
139
|
+
if mode == ExecutionMode.DOCKER:
|
|
140
|
+
result = self._execute_docker(command, timeout, callback, **kwargs)
|
|
141
|
+
else:
|
|
142
|
+
result = self._execute_local(command, timeout, callback, **kwargs)
|
|
143
|
+
|
|
144
|
+
# Parse output
|
|
145
|
+
findings = []
|
|
146
|
+
if parse and result.output:
|
|
147
|
+
detected_tool = tool_name or self._detect_tool(command)
|
|
148
|
+
findings = self.parser.parse(result.output, detected_tool)
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
"result": result,
|
|
152
|
+
"findings": findings,
|
|
153
|
+
"mode": mode,
|
|
154
|
+
"tool": tool_name or self._detect_tool(command),
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async def execute_async(
|
|
158
|
+
self,
|
|
159
|
+
command: str,
|
|
160
|
+
**kwargs
|
|
161
|
+
) -> Dict[str, Any]:
|
|
162
|
+
"""Async version of execute"""
|
|
163
|
+
loop = asyncio.get_event_loop()
|
|
164
|
+
return await loop.run_in_executor(
|
|
165
|
+
None,
|
|
166
|
+
lambda: self.execute(command, **kwargs)
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
def execute_batch(
|
|
170
|
+
self,
|
|
171
|
+
commands: List[str],
|
|
172
|
+
parallel: bool = False,
|
|
173
|
+
**kwargs
|
|
174
|
+
) -> List[Dict[str, Any]]:
|
|
175
|
+
"""
|
|
176
|
+
Execute multiple commands.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
commands: List of commands
|
|
180
|
+
parallel: Execute in parallel (async)
|
|
181
|
+
**kwargs: Execution options
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
List of execution results
|
|
185
|
+
"""
|
|
186
|
+
if parallel:
|
|
187
|
+
return asyncio.run(self._execute_batch_async(commands, **kwargs))
|
|
188
|
+
|
|
189
|
+
return [self.execute(cmd, **kwargs) for cmd in commands]
|
|
190
|
+
|
|
191
|
+
async def _execute_batch_async(
|
|
192
|
+
self,
|
|
193
|
+
commands: List[str],
|
|
194
|
+
**kwargs
|
|
195
|
+
) -> List[Dict[str, Any]]:
|
|
196
|
+
"""Execute commands in parallel"""
|
|
197
|
+
tasks = [self.execute_async(cmd, **kwargs) for cmd in commands]
|
|
198
|
+
return await asyncio.gather(*tasks)
|
|
199
|
+
|
|
200
|
+
def _execute_local(
|
|
201
|
+
self,
|
|
202
|
+
command: str,
|
|
203
|
+
timeout: int,
|
|
204
|
+
callback: Optional[Callable] = None,
|
|
205
|
+
**kwargs
|
|
206
|
+
) -> ExecutionResult:
|
|
207
|
+
"""Execute command locally"""
|
|
208
|
+
if callback:
|
|
209
|
+
return self.terminal.execute_streaming(command, callback, timeout)
|
|
210
|
+
return self.terminal.execute(command, timeout, **kwargs)
|
|
211
|
+
|
|
212
|
+
def _execute_docker(
|
|
213
|
+
self,
|
|
214
|
+
command: str,
|
|
215
|
+
timeout: int,
|
|
216
|
+
callback: Optional[Callable] = None,
|
|
217
|
+
**kwargs
|
|
218
|
+
) -> ExecutionResult:
|
|
219
|
+
"""Execute command in Docker sandbox"""
|
|
220
|
+
if not self.sandbox:
|
|
221
|
+
# Fallback to local if Docker unavailable
|
|
222
|
+
return self._execute_local(command, timeout, callback, **kwargs)
|
|
223
|
+
|
|
224
|
+
if callback:
|
|
225
|
+
result = self.sandbox.execute_streaming(command, callback, timeout)
|
|
226
|
+
else:
|
|
227
|
+
result = self.sandbox.execute(command, timeout)
|
|
228
|
+
|
|
229
|
+
# Convert SandboxResult to ExecutionResult
|
|
230
|
+
return ExecutionResult(
|
|
231
|
+
command=command,
|
|
232
|
+
output=result.output,
|
|
233
|
+
error=result.error,
|
|
234
|
+
return_code=result.return_code,
|
|
235
|
+
timed_out=result.error and "timed out" in result.error.lower() if result.error else False,
|
|
236
|
+
duration=result.duration,
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
def _select_mode(self, command: str, tool_name: Optional[str] = None) -> ExecutionMode:
|
|
240
|
+
"""Auto-select execution mode based on command/tool"""
|
|
241
|
+
tool = tool_name or self._detect_tool(command)
|
|
242
|
+
|
|
243
|
+
if tool in self.SANDBOX_TOOLS:
|
|
244
|
+
return ExecutionMode.DOCKER if self.sandbox else ExecutionMode.LOCAL
|
|
245
|
+
|
|
246
|
+
if tool in self.LOCAL_TOOLS:
|
|
247
|
+
return ExecutionMode.LOCAL
|
|
248
|
+
|
|
249
|
+
# Check for dangerous patterns
|
|
250
|
+
dangerous_patterns = [
|
|
251
|
+
"rm -rf", "mkfs", "dd if=", "> /dev/",
|
|
252
|
+
"chmod 777", "wget -O - | sh", "curl | bash",
|
|
253
|
+
]
|
|
254
|
+
for pattern in dangerous_patterns:
|
|
255
|
+
if pattern in command:
|
|
256
|
+
return ExecutionMode.DOCKER if self.sandbox else ExecutionMode.LOCAL
|
|
257
|
+
|
|
258
|
+
return ExecutionMode.LOCAL
|
|
259
|
+
|
|
260
|
+
def _detect_tool(self, command: str) -> str:
|
|
261
|
+
"""Detect tool name from command"""
|
|
262
|
+
parts = command.strip().split()
|
|
263
|
+
if not parts:
|
|
264
|
+
return "unknown"
|
|
265
|
+
|
|
266
|
+
tool = parts[0]
|
|
267
|
+
|
|
268
|
+
# Handle sudo, env, etc.
|
|
269
|
+
if tool in ["sudo", "env", "time", "timeout"]:
|
|
270
|
+
if len(parts) > 1:
|
|
271
|
+
tool = parts[1]
|
|
272
|
+
|
|
273
|
+
# Handle full paths
|
|
274
|
+
if "/" in tool:
|
|
275
|
+
tool = tool.split("/")[-1]
|
|
276
|
+
|
|
277
|
+
return tool
|
|
278
|
+
|
|
279
|
+
def check_tool(self, tool_name: str) -> Dict[str, Any]:
|
|
280
|
+
"""Check if a tool is available"""
|
|
281
|
+
available = self.terminal.check_tool_available(tool_name)
|
|
282
|
+
version = self.terminal.get_tool_version(tool_name) if available else None
|
|
283
|
+
|
|
284
|
+
return {
|
|
285
|
+
"tool": tool_name,
|
|
286
|
+
"available": available,
|
|
287
|
+
"version": version,
|
|
288
|
+
"recommended_mode": (
|
|
289
|
+
ExecutionMode.DOCKER if tool_name in self.SANDBOX_TOOLS
|
|
290
|
+
else ExecutionMode.LOCAL
|
|
291
|
+
),
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
def check_tools(self, tools: List[str]) -> Dict[str, Dict[str, Any]]:
|
|
295
|
+
"""Check availability of multiple tools"""
|
|
296
|
+
return {tool: self.check_tool(tool) for tool in tools}
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
# Factory function
|
|
300
|
+
def get_execution_engine(**kwargs) -> ExecutionEngine:
|
|
301
|
+
"""Create and return execution engine"""
|
|
302
|
+
return ExecutionEngine(**kwargs)
|