janito 2.22.0__py3-none-any.whl → 2.24.1__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.
- janito/README.md +0 -0
- janito/agent/setup_agent.py +14 -0
- janito/agent/templates/profiles/system_prompt_template_Developer_with_Python_Tools.txt.j2 +59 -11
- janito/agent/templates/profiles/system_prompt_template_developer.txt.j2 +53 -7
- janito/agent/templates/profiles/system_prompt_template_market_analyst.txt.j2 +108 -8
- janito/agent/templates/profiles/system_prompt_template_model_conversation_without_tools_or_context.txt.j2 +53 -1
- janito/cli/chat_mode/session.py +8 -1
- janito/cli/chat_mode/shell/commands/__init__.py +2 -0
- janito/cli/chat_mode/shell/commands/security/__init__.py +1 -0
- janito/cli/chat_mode/shell/commands/security/allowed_sites.py +94 -0
- janito/cli/chat_mode/shell/commands/security_command.py +51 -0
- janito/cli/chat_mode/shell/commands.bak.zip +0 -0
- janito/cli/chat_mode/shell/session.bak.zip +0 -0
- janito/cli/cli_commands/list_plugins.py +45 -0
- janito/cli/cli_commands/show_system_prompt.py +13 -40
- janito/cli/core/getters.py +4 -0
- janito/cli/core/runner.py +7 -2
- janito/cli/core/setters.py +10 -1
- janito/cli/main_cli.py +25 -3
- janito/cli/single_shot_mode/handler.py +3 -1
- janito/config_manager.py +10 -0
- janito/docs/GETTING_STARTED.md +0 -0
- janito/drivers/dashscope.bak.zip +0 -0
- janito/drivers/openai/README.md +0 -0
- janito/drivers/openai_responses.bak.zip +0 -0
- janito/llm/README.md +0 -0
- janito/mkdocs.yml +0 -0
- janito/plugins/__init__.py +17 -0
- janito/plugins/base.py +93 -0
- janito/plugins/discovery.py +160 -0
- janito/plugins/manager.py +185 -0
- janito/providers/dashscope.bak.zip +0 -0
- janito/providers/ibm/README.md +0 -0
- janito/shell.bak.zip +0 -0
- janito/tools/DOCSTRING_STANDARD.txt +0 -0
- janito/tools/README.md +0 -0
- janito/tools/adapters/local/__init__.py +2 -0
- janito/tools/adapters/local/adapter.py +55 -0
- janito/tools/adapters/local/ask_user.py +2 -0
- janito/tools/adapters/local/fetch_url.py +89 -4
- janito/tools/adapters/local/find_files.py +2 -0
- janito/tools/adapters/local/get_file_outline/core.py +2 -0
- janito/tools/adapters/local/get_file_outline/search_outline.py +2 -0
- janito/tools/adapters/local/open_html_in_browser.py +2 -0
- janito/tools/adapters/local/open_url.py +2 -0
- janito/tools/adapters/local/python_code_run.py +15 -10
- janito/tools/adapters/local/python_command_run.py +14 -9
- janito/tools/adapters/local/python_file_run.py +15 -10
- janito/tools/adapters/local/read_chart.py +252 -0
- janito/tools/adapters/local/read_files.py +2 -0
- janito/tools/adapters/local/replace_text_in_file.py +1 -1
- janito/tools/adapters/local/run_bash_command.py +18 -12
- janito/tools/adapters/local/run_powershell_command.py +15 -9
- janito/tools/adapters/local/search_text/core.py +2 -0
- janito/tools/adapters/local/validate_file_syntax/core.py +6 -0
- janito/tools/adapters/local/validate_file_syntax/jinja2_validator.py +47 -0
- janito/tools/adapters/local/view_file.py +2 -0
- janito/tools/loop_protection.py +115 -0
- janito/tools/loop_protection_decorator.py +110 -0
- janito/tools/outline_file.bak.zip +0 -0
- janito/tools/url_whitelist.py +121 -0
- {janito-2.22.0.dist-info → janito-2.24.1.dist-info}/METADATA +411 -411
- {janito-2.22.0.dist-info → janito-2.24.1.dist-info}/RECORD +52 -39
- {janito-2.22.0.dist-info → janito-2.24.1.dist-info}/entry_points.txt +0 -0
- {janito-2.22.0.dist-info → janito-2.24.1.dist-info}/licenses/LICENSE +0 -0
- {janito-2.22.0.dist-info → janito-2.24.1.dist-info}/top_level.txt +0 -0
- {janito-2.22.0.dist-info → janito-2.24.1.dist-info}/WHEEL +0 -0
@@ -1,5 +1,6 @@
|
|
1
1
|
from typing import Type, Dict, Any
|
2
2
|
from janito.tools.tools_adapter import ToolsAdapterBase as ToolsAdapter
|
3
|
+
from janito.tools.tool_use_tracker import ToolUseTracker
|
3
4
|
|
4
5
|
|
5
6
|
class LocalToolsAdapter(ToolsAdapter):
|
@@ -58,6 +59,9 @@ class LocalToolsAdapter(ToolsAdapter):
|
|
58
59
|
# consistency with many file-system tools.
|
59
60
|
os.chdir(self.workdir)
|
60
61
|
|
62
|
+
# Initialize tool tracker
|
63
|
+
self.tool_tracker = ToolUseTracker.instance()
|
64
|
+
|
61
65
|
if tools:
|
62
66
|
for tool in tools:
|
63
67
|
self.register_tool(tool)
|
@@ -130,6 +134,57 @@ class LocalToolsAdapter(ToolsAdapter):
|
|
130
134
|
and not is_tool_disabled(entry["instance"].tool_name)
|
131
135
|
]
|
132
136
|
|
137
|
+
# ------------------------------------------------------------------
|
138
|
+
# Tool execution with error handling
|
139
|
+
# ------------------------------------------------------------------
|
140
|
+
def execute_tool(self, name: str, **kwargs):
|
141
|
+
"""
|
142
|
+
Execute a tool with proper error handling.
|
143
|
+
|
144
|
+
This method extends the base execute_tool functionality by adding
|
145
|
+
error handling for RuntimeError exceptions that may be raised by
|
146
|
+
tools with loop protection decorators.
|
147
|
+
|
148
|
+
Args:
|
149
|
+
name: The name of the tool to execute
|
150
|
+
**kwargs: Arguments to pass to the tool
|
151
|
+
|
152
|
+
Returns:
|
153
|
+
The result of the tool execution
|
154
|
+
|
155
|
+
Raises:
|
156
|
+
ToolCallException: If tool execution fails for any reason
|
157
|
+
ValueError: If the tool is not found or not allowed
|
158
|
+
"""
|
159
|
+
# First check if tool exists and is allowed
|
160
|
+
tool = self.get_tool(name)
|
161
|
+
if not tool:
|
162
|
+
raise ValueError(f"Tool '{name}' not found or not allowed.")
|
163
|
+
|
164
|
+
# Record tool usage
|
165
|
+
self.tool_tracker.record(name, kwargs)
|
166
|
+
|
167
|
+
# Execute the tool and handle any RuntimeError from loop protection
|
168
|
+
try:
|
169
|
+
return super().execute_tool(name, **kwargs)
|
170
|
+
except RuntimeError as e:
|
171
|
+
# Check if this is a loop protection error
|
172
|
+
if "Loop protection:" in str(e):
|
173
|
+
# Re-raise as ToolCallException to maintain consistent error flow
|
174
|
+
from janito.exceptions import ToolCallException
|
175
|
+
raise ToolCallException(
|
176
|
+
name,
|
177
|
+
f"Loop protection triggered: {str(e)}",
|
178
|
+
arguments=kwargs
|
179
|
+
)
|
180
|
+
# Re-raise other RuntimeError exceptions as ToolCallException
|
181
|
+
from janito.exceptions import ToolCallException
|
182
|
+
raise ToolCallException(
|
183
|
+
name,
|
184
|
+
f"Runtime error during tool execution: {str(e)}",
|
185
|
+
arguments=kwargs
|
186
|
+
)
|
187
|
+
|
133
188
|
# ------------------------------------------------------------------
|
134
189
|
# Convenience methods
|
135
190
|
# ------------------------------------------------------------------
|
@@ -1,5 +1,6 @@
|
|
1
1
|
from janito.tools.tool_base import ToolBase, ToolPermissions
|
2
2
|
from janito.tools.adapters.local.adapter import register_local_tool
|
3
|
+
from janito.tools.loop_protection_decorator import protect_against_loops
|
3
4
|
|
4
5
|
from rich import print as rich_print
|
5
6
|
from janito.i18n import tr
|
@@ -31,6 +32,7 @@ class AskUserTool(ToolBase):
|
|
31
32
|
permissions = ToolPermissions(read=True)
|
32
33
|
tool_name = "ask_user"
|
33
34
|
|
35
|
+
@protect_against_loops(max_calls=5, time_window=10.0)
|
34
36
|
def run(self, question: str) -> str:
|
35
37
|
|
36
38
|
print() # Print an empty line before the question panel
|
@@ -9,6 +9,7 @@ from janito.tools.tool_base import ToolBase, ToolPermissions
|
|
9
9
|
from janito.report_events import ReportAction
|
10
10
|
from janito.i18n import tr
|
11
11
|
from janito.tools.tool_utils import pluralize
|
12
|
+
from janito.tools.loop_protection_decorator import protect_against_loops
|
12
13
|
|
13
14
|
|
14
15
|
@register_local_tool
|
@@ -16,6 +17,25 @@ class FetchUrlTool(ToolBase):
|
|
16
17
|
"""
|
17
18
|
Fetch the content of a web page and extract its text.
|
18
19
|
|
20
|
+
This tool implements a **session-based caching mechanism** that provides
|
21
|
+
**in-memory caching** for the lifetime of the tool instance. URLs are cached
|
22
|
+
in RAM during the session, providing instant access to previously fetched
|
23
|
+
content without making additional HTTP requests.
|
24
|
+
|
25
|
+
**Session Cache Behavior:**
|
26
|
+
- **Lifetime**: Cache exists for the lifetime of the FetchUrlTool instance
|
27
|
+
- **Scope**: In-memory (RAM) cache, not persisted to disk
|
28
|
+
- **Storage**: Successful responses are cached as raw HTML content
|
29
|
+
- **Key**: Cache key is the exact URL string
|
30
|
+
- **Invalidation**: Cache is automatically cleared when the tool instance is destroyed
|
31
|
+
- **Performance**: Subsequent requests for the same URL return instantly
|
32
|
+
|
33
|
+
**Error Cache Behavior:**
|
34
|
+
- HTTP 403 errors: Cached for 24 hours (more permanent)
|
35
|
+
- HTTP 404 errors: Cached for 1 hour (temporary)
|
36
|
+
- Other 4xx errors: Cached for 30 minutes
|
37
|
+
- 5xx errors: Not cached (retried on each request)
|
38
|
+
|
19
39
|
Args:
|
20
40
|
url (str): The URL of the web page to fetch.
|
21
41
|
search_strings (list[str], optional): Strings to search for in the page content.
|
@@ -23,6 +43,8 @@ class FetchUrlTool(ToolBase):
|
|
23
43
|
max_lines (int, optional): Maximum number of lines to return. Defaults to 200.
|
24
44
|
context_chars (int, optional): Characters of context around search matches. Defaults to 400.
|
25
45
|
timeout (int, optional): Timeout in seconds for the HTTP request. Defaults to 10.
|
46
|
+
save_to_file (str, optional): File path to save the full resource content. If provided,
|
47
|
+
the complete response will be saved to this file instead of being processed.
|
26
48
|
Returns:
|
27
49
|
str: Extracted text content from the web page, or a warning message. Example:
|
28
50
|
- "<main text content...>"
|
@@ -38,6 +60,7 @@ class FetchUrlTool(ToolBase):
|
|
38
60
|
self.cache_dir = Path.home() / ".janito" / "cache" / "fetch_url"
|
39
61
|
self.cache_dir.mkdir(parents=True, exist_ok=True)
|
40
62
|
self.cache_file = self.cache_dir / "error_cache.json"
|
63
|
+
self.session_cache = {} # In-memory session cache - lifetime matches tool instance
|
41
64
|
self._load_cache()
|
42
65
|
|
43
66
|
def _load_cache(self):
|
@@ -99,8 +122,41 @@ class FetchUrlTool(ToolBase):
|
|
99
122
|
self._save_cache()
|
100
123
|
|
101
124
|
def _fetch_url_content(self, url: str, timeout: int = 10) -> str:
|
102
|
-
"""Fetch URL content and handle HTTP errors.
|
103
|
-
|
125
|
+
"""Fetch URL content and handle HTTP errors.
|
126
|
+
|
127
|
+
Implements two-tier caching:
|
128
|
+
1. Session cache: In-memory cache for successful responses (lifetime = tool instance)
|
129
|
+
2. Error cache: Persistent disk cache for HTTP errors with different expiration times
|
130
|
+
|
131
|
+
Also implements URL whitelist checking.
|
132
|
+
"""
|
133
|
+
# Check URL whitelist
|
134
|
+
from janito.tools.url_whitelist import get_url_whitelist_manager
|
135
|
+
whitelist_manager = get_url_whitelist_manager()
|
136
|
+
|
137
|
+
if not whitelist_manager.is_url_allowed(url):
|
138
|
+
error_message = tr(
|
139
|
+
"Warning: URL blocked by whitelist: {url}",
|
140
|
+
url=url,
|
141
|
+
)
|
142
|
+
self.report_error(
|
143
|
+
tr(
|
144
|
+
"❗ URL blocked by whitelist: {url}",
|
145
|
+
url=url,
|
146
|
+
),
|
147
|
+
ReportAction.READ,
|
148
|
+
)
|
149
|
+
return error_message
|
150
|
+
|
151
|
+
# Check session cache first
|
152
|
+
if url in self.session_cache:
|
153
|
+
self.report_warning(
|
154
|
+
tr("ℹ️ Using session cache"),
|
155
|
+
ReportAction.READ,
|
156
|
+
)
|
157
|
+
return self.session_cache[url]
|
158
|
+
|
159
|
+
# Check persistent cache for known errors
|
104
160
|
cached_error, is_cached = self._get_cached_error(url)
|
105
161
|
if cached_error:
|
106
162
|
self.report_warning(
|
@@ -115,7 +171,10 @@ class FetchUrlTool(ToolBase):
|
|
115
171
|
try:
|
116
172
|
response = requests.get(url, timeout=timeout)
|
117
173
|
response.raise_for_status()
|
118
|
-
|
174
|
+
content = response.text
|
175
|
+
# Cache successful responses in session cache
|
176
|
+
self.session_cache[url] = content
|
177
|
+
return content
|
119
178
|
except requests.exceptions.HTTPError as http_err:
|
120
179
|
status_code = http_err.response.status_code if http_err.response else None
|
121
180
|
if status_code and 400 <= status_code < 500:
|
@@ -203,6 +262,7 @@ class FetchUrlTool(ToolBase):
|
|
203
262
|
|
204
263
|
return text
|
205
264
|
|
265
|
+
@protect_against_loops(max_calls=5, time_window=10.0)
|
206
266
|
def run(
|
207
267
|
self,
|
208
268
|
url: str,
|
@@ -211,6 +271,7 @@ class FetchUrlTool(ToolBase):
|
|
211
271
|
max_lines: int = 200,
|
212
272
|
context_chars: int = 400,
|
213
273
|
timeout: int = 10,
|
274
|
+
save_to_file: str = None,
|
214
275
|
) -> str:
|
215
276
|
if not url.strip():
|
216
277
|
self.report_warning(tr("ℹ️ Empty URL provided."), ReportAction.READ)
|
@@ -218,7 +279,31 @@ class FetchUrlTool(ToolBase):
|
|
218
279
|
|
219
280
|
self.report_action(tr("🌐 Fetch URL '{url}' ...", url=url), ReportAction.READ)
|
220
281
|
|
221
|
-
#
|
282
|
+
# Check if we should save to file
|
283
|
+
if save_to_file:
|
284
|
+
html_content = self._fetch_url_content(url, timeout=timeout)
|
285
|
+
if html_content.startswith("Warning:"):
|
286
|
+
return html_content
|
287
|
+
|
288
|
+
try:
|
289
|
+
with open(save_to_file, 'w', encoding='utf-8') as f:
|
290
|
+
f.write(html_content)
|
291
|
+
file_size = len(html_content)
|
292
|
+
self.report_success(
|
293
|
+
tr(
|
294
|
+
"✅ Saved {size} bytes to {file}",
|
295
|
+
size=file_size,
|
296
|
+
file=save_to_file,
|
297
|
+
),
|
298
|
+
ReportAction.READ,
|
299
|
+
)
|
300
|
+
return tr("Successfully saved content to: {file}", file=save_to_file)
|
301
|
+
except IOError as e:
|
302
|
+
error_msg = tr("Error saving to file: {error}", error=str(e))
|
303
|
+
self.report_error(error_msg, ReportAction.READ)
|
304
|
+
return error_msg
|
305
|
+
|
306
|
+
# Normal processing path
|
222
307
|
html_content = self._fetch_url_content(url, timeout=timeout)
|
223
308
|
if html_content.startswith("Warning:"):
|
224
309
|
return html_content
|
@@ -6,6 +6,7 @@ from janito.dir_walk_utils import walk_dir_with_gitignore
|
|
6
6
|
from janito.i18n import tr
|
7
7
|
import fnmatch
|
8
8
|
import os
|
9
|
+
from janito.tools.loop_protection_decorator import protect_against_loops
|
9
10
|
|
10
11
|
|
11
12
|
@register_local_tool
|
@@ -107,6 +108,7 @@ class FindFilesTool(ToolBase):
|
|
107
108
|
}
|
108
109
|
return sorted(dir_output)
|
109
110
|
|
111
|
+
@protect_against_loops(max_calls=5, time_window=10.0)
|
110
112
|
def run(
|
111
113
|
self,
|
112
114
|
paths: str,
|
@@ -10,6 +10,7 @@ from janito.tools.tool_utils import display_path, pluralize
|
|
10
10
|
from janito.i18n import tr
|
11
11
|
|
12
12
|
from janito.tools.adapters.local.adapter import register_local_tool as register_tool
|
13
|
+
from janito.tools.loop_protection_decorator import protect_against_loops
|
13
14
|
|
14
15
|
|
15
16
|
@register_tool
|
@@ -24,6 +25,7 @@ class GetFileOutlineTool(ToolBase):
|
|
24
25
|
permissions = ToolPermissions(read=True)
|
25
26
|
tool_name = "get_file_outline"
|
26
27
|
|
28
|
+
@protect_against_loops(max_calls=5, time_window=10.0)
|
27
29
|
def run(self, path: str) -> str:
|
28
30
|
try:
|
29
31
|
self.report_action(
|
@@ -1,5 +1,6 @@
|
|
1
1
|
from janito.tools.tool_base import ToolBase, ToolPermissions
|
2
2
|
from janito.report_events import ReportAction
|
3
|
+
from janito.tools.loop_protection_decorator import protect_against_loops
|
3
4
|
|
4
5
|
|
5
6
|
class SearchOutlineTool(ToolBase):
|
@@ -15,6 +16,7 @@ class SearchOutlineTool(ToolBase):
|
|
15
16
|
permissions = ToolPermissions(read=True)
|
16
17
|
tool_name = "search_outline"
|
17
18
|
|
19
|
+
@protect_against_loops(max_calls=5, time_window=10.0)
|
18
20
|
def run(self, path: str) -> str:
|
19
21
|
from janito.tools.tool_utils import display_path
|
20
22
|
from janito.i18n import tr
|
@@ -4,6 +4,7 @@ from janito.tools.adapters.local.adapter import register_local_tool
|
|
4
4
|
from janito.tools.tool_base import ToolBase, ToolPermissions
|
5
5
|
from janito.report_events import ReportAction
|
6
6
|
from janito.i18n import tr
|
7
|
+
from janito.tools.loop_protection_decorator import protect_against_loops
|
7
8
|
|
8
9
|
|
9
10
|
@register_local_tool
|
@@ -20,6 +21,7 @@ class OpenHtmlInBrowserTool(ToolBase):
|
|
20
21
|
permissions = ToolPermissions(read=True)
|
21
22
|
tool_name = "open_html_in_browser"
|
22
23
|
|
24
|
+
@protect_against_loops(max_calls=5, time_window=10.0)
|
23
25
|
def run(self, path: str) -> str:
|
24
26
|
if not path.strip():
|
25
27
|
self.report_warning(tr("ℹ️ Empty file path provided."))
|
@@ -3,6 +3,7 @@ from janito.tools.adapters.local.adapter import register_local_tool
|
|
3
3
|
from janito.tools.tool_base import ToolBase, ToolPermissions
|
4
4
|
from janito.report_events import ReportAction
|
5
5
|
from janito.i18n import tr
|
6
|
+
from janito.tools.loop_protection_decorator import protect_against_loops
|
6
7
|
|
7
8
|
|
8
9
|
@register_local_tool
|
@@ -19,6 +20,7 @@ class OpenUrlTool(ToolBase):
|
|
19
20
|
permissions = ToolPermissions(read=True)
|
20
21
|
tool_name = "open_url"
|
21
22
|
|
23
|
+
@protect_against_loops(max_calls=5, time_window=10.0)
|
22
24
|
def run(self, url: str) -> str:
|
23
25
|
if not url.strip():
|
24
26
|
self.report_warning(tr("ℹ️ Empty URL provided."))
|
@@ -17,6 +17,7 @@ class PythonCodeRunTool(ToolBase):
|
|
17
17
|
Args:
|
18
18
|
code (str): The Python code to execute as a string.
|
19
19
|
timeout (int): Timeout in seconds for the command. Defaults to 60.
|
20
|
+
silent (bool): If True, suppresses progress and status messages. Defaults to False.
|
20
21
|
|
21
22
|
Returns:
|
22
23
|
str: Output and status message, or file paths/line counts if output is large.
|
@@ -25,15 +26,18 @@ class PythonCodeRunTool(ToolBase):
|
|
25
26
|
permissions = ToolPermissions(execute=True)
|
26
27
|
tool_name = "python_code_run"
|
27
28
|
|
28
|
-
def run(self, code: str, timeout: int = 60) -> str:
|
29
|
+
def run(self, code: str, timeout: int = 60, silent: bool = False) -> str:
|
29
30
|
if not code.strip():
|
30
31
|
self.report_warning(tr("ℹ️ Empty code provided."), ReportAction.EXECUTE)
|
31
32
|
return tr("Warning: Empty code provided. Operation skipped.")
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
33
|
+
if not silent:
|
34
|
+
self.report_action(
|
35
|
+
tr("⚡ Running: python (stdin mode) ...\n{code}\n", code=code),
|
36
|
+
ReportAction.EXECUTE,
|
37
|
+
)
|
38
|
+
self.report_stdout("\n")
|
39
|
+
else:
|
40
|
+
self.report_action(tr("⚡ Executing..."), ReportAction.EXECUTE)
|
37
41
|
try:
|
38
42
|
with (
|
39
43
|
tempfile.NamedTemporaryFile(
|
@@ -70,10 +74,11 @@ class PythonCodeRunTool(ToolBase):
|
|
70
74
|
)
|
71
75
|
stdout_file.flush()
|
72
76
|
stderr_file.flush()
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
+
if not silent:
|
78
|
+
self.report_success(
|
79
|
+
tr("✅ Return code {return_code}", return_code=return_code),
|
80
|
+
ReportAction.EXECUTE,
|
81
|
+
)
|
77
82
|
return self._format_result(
|
78
83
|
stdout_file.name, stderr_file.name, return_code
|
79
84
|
)
|
@@ -17,6 +17,7 @@ class PythonCommandRunTool(ToolBase):
|
|
17
17
|
Args:
|
18
18
|
code (str): The Python code to execute as a string.
|
19
19
|
timeout (int): Timeout in seconds for the command. Defaults to 60.
|
20
|
+
silent (bool): If True, suppresses progress and status messages. Defaults to False.
|
20
21
|
|
21
22
|
Returns:
|
22
23
|
str: Output and status message, or file paths/line counts if output is large.
|
@@ -25,14 +26,17 @@ class PythonCommandRunTool(ToolBase):
|
|
25
26
|
permissions = ToolPermissions(execute=True)
|
26
27
|
tool_name = "python_command_run"
|
27
28
|
|
28
|
-
def run(self, code: str, timeout: int = 60) -> str:
|
29
|
+
def run(self, code: str, timeout: int = 60, silent: bool = False) -> str:
|
29
30
|
if not code.strip():
|
30
31
|
self.report_warning(tr("ℹ️ Empty code provided."), ReportAction.EXECUTE)
|
31
32
|
return tr("Warning: Empty code provided. Operation skipped.")
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
if not silent:
|
34
|
+
self.report_action(
|
35
|
+
tr("🐍 Running: python -c ...\n{code}\n", code=code), ReportAction.EXECUTE
|
36
|
+
)
|
37
|
+
self.report_stdout("\n")
|
38
|
+
else:
|
39
|
+
self.report_action(tr("⚡ Executing..."), ReportAction.EXECUTE)
|
36
40
|
try:
|
37
41
|
with (
|
38
42
|
tempfile.NamedTemporaryFile(
|
@@ -68,10 +72,11 @@ class PythonCommandRunTool(ToolBase):
|
|
68
72
|
)
|
69
73
|
stdout_file.flush()
|
70
74
|
stderr_file.flush()
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
+
if not silent:
|
76
|
+
self.report_success(
|
77
|
+
tr("✅ Return code {return_code}", return_code=return_code),
|
78
|
+
ReportAction.EXECUTE,
|
79
|
+
)
|
75
80
|
return self._format_result(
|
76
81
|
stdout_file.name, stderr_file.name, return_code
|
77
82
|
)
|
@@ -17,6 +17,7 @@ class PythonFileRunTool(ToolBase):
|
|
17
17
|
Args:
|
18
18
|
path (str): Path to the Python script file to execute.
|
19
19
|
timeout (int): Timeout in seconds for the command. Defaults to 60.
|
20
|
+
silent (bool): If True, suppresses progress and status messages. Defaults to False.
|
20
21
|
|
21
22
|
Returns:
|
22
23
|
str: Output and status message, or file paths/line counts if output is large.
|
@@ -25,12 +26,15 @@ class PythonFileRunTool(ToolBase):
|
|
25
26
|
permissions = ToolPermissions(execute=True)
|
26
27
|
tool_name = "python_file_run"
|
27
28
|
|
28
|
-
def run(self, path: str, timeout: int = 60) -> str:
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
29
|
+
def run(self, path: str, timeout: int = 60, silent: bool = False) -> str:
|
30
|
+
if not silent:
|
31
|
+
self.report_action(
|
32
|
+
tr("🚀 Running: python {path}", path=path),
|
33
|
+
ReportAction.EXECUTE,
|
34
|
+
)
|
35
|
+
self.report_stdout("\n")
|
36
|
+
else:
|
37
|
+
self.report_action(tr("⚡ Executing..."), ReportAction.EXECUTE)
|
34
38
|
try:
|
35
39
|
with (
|
36
40
|
tempfile.NamedTemporaryFile(
|
@@ -66,10 +70,11 @@ class PythonFileRunTool(ToolBase):
|
|
66
70
|
)
|
67
71
|
stdout_file.flush()
|
68
72
|
stderr_file.flush()
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
+
if not silent:
|
74
|
+
self.report_success(
|
75
|
+
tr("✅ Return code {return_code}", return_code=return_code),
|
76
|
+
ReportAction.EXECUTE,
|
77
|
+
)
|
73
78
|
return self._format_result(
|
74
79
|
stdout_file.name, stderr_file.name, return_code
|
75
80
|
)
|