janito 1.10.0__py3-none-any.whl → 1.11.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/__init__.py +1 -1
- janito/agent/conversation_api.py +178 -90
- janito/agent/conversation_ui.py +1 -1
- janito/agent/llm_conversation_history.py +12 -0
- janito/agent/templates/profiles/system_prompt_template_base.txt.j2 +19 -4
- janito/agent/tools/__init__.py +2 -0
- janito/agent/tools/create_directory.py +1 -1
- janito/agent/tools/create_file.py +1 -1
- janito/agent/tools/fetch_url.py +1 -1
- janito/agent/tools/find_files.py +26 -13
- janito/agent/tools/get_file_outline/core.py +1 -1
- janito/agent/tools/get_file_outline/python_outline.py +139 -95
- janito/agent/tools/get_lines.py +92 -63
- janito/agent/tools/move_file.py +58 -32
- janito/agent/tools/open_url.py +31 -0
- janito/agent/tools/python_command_runner.py +85 -86
- janito/agent/tools/python_file_runner.py +85 -86
- janito/agent/tools/python_stdin_runner.py +87 -88
- janito/agent/tools/remove_directory.py +1 -1
- janito/agent/tools/remove_file.py +1 -1
- janito/agent/tools/replace_file.py +2 -2
- janito/agent/tools/replace_text_in_file.py +193 -149
- janito/agent/tools/run_bash_command.py +1 -1
- janito/agent/tools/run_powershell_command.py +4 -0
- janito/agent/tools/search_text/__init__.py +1 -0
- janito/agent/tools/search_text/core.py +176 -0
- janito/agent/tools/search_text/match_lines.py +58 -0
- janito/agent/tools/search_text/pattern_utils.py +65 -0
- janito/agent/tools/search_text/traverse_directory.py +132 -0
- janito/agent/tools/validate_file_syntax/core.py +41 -30
- janito/agent/tools/validate_file_syntax/html_validator.py +21 -5
- janito/agent/tools/validate_file_syntax/markdown_validator.py +77 -34
- janito/agent/tools_utils/gitignore_utils.py +25 -2
- janito/agent/tools_utils/utils.py +7 -1
- janito/cli/config_commands.py +112 -109
- janito/shell/main.py +51 -8
- janito/shell/session/config.py +83 -75
- janito/shell/ui/interactive.py +97 -73
- janito/termweb/static/editor.css +32 -29
- janito/termweb/static/editor.css.bak +140 -22
- janito/termweb/static/editor.html +12 -7
- janito/termweb/static/editor.html.bak +16 -11
- janito/termweb/static/editor.js +94 -40
- janito/termweb/static/editor.js.bak +97 -65
- janito/termweb/static/index.html +1 -2
- janito/termweb/static/index.html.bak +1 -1
- janito/termweb/static/termweb.css +1 -22
- janito/termweb/static/termweb.css.bak +6 -4
- janito/termweb/static/termweb.js +0 -6
- janito/termweb/static/termweb.js.bak +1 -2
- {janito-1.10.0.dist-info → janito-1.11.1.dist-info}/METADATA +1 -1
- {janito-1.10.0.dist-info → janito-1.11.1.dist-info}/RECORD +56 -51
- {janito-1.10.0.dist-info → janito-1.11.1.dist-info}/WHEEL +1 -1
- janito/agent/tools/search_text.py +0 -254
- {janito-1.10.0.dist-info → janito-1.11.1.dist-info}/entry_points.txt +0 -0
- {janito-1.10.0.dist-info → janito-1.11.1.dist-info}/licenses/LICENSE +0 -0
- {janito-1.10.0.dist-info → janito-1.11.1.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,5 @@
|
|
1
1
|
import subprocess
|
2
|
+
import os
|
2
3
|
import sys
|
3
4
|
import tempfile
|
4
5
|
import threading
|
@@ -49,102 +50,100 @@ class PythonCommandRunnerTool(ToolBase):
|
|
49
50
|
bufsize=1,
|
50
51
|
universal_newlines=True,
|
51
52
|
encoding="utf-8",
|
52
|
-
env={**
|
53
|
+
env={**os.environ, "PYTHONIOENCODING": "utf-8"},
|
53
54
|
)
|
54
|
-
stdout_lines =
|
55
|
-
|
56
|
-
|
57
|
-
def stream_output(stream, file_obj, report_func, count_func):
|
58
|
-
nonlocal stdout_lines, stderr_lines
|
59
|
-
for line in stream:
|
60
|
-
file_obj.write(line)
|
61
|
-
file_obj.flush()
|
62
|
-
report_func(line)
|
63
|
-
if count_func == "stdout":
|
64
|
-
stdout_lines += 1
|
65
|
-
else:
|
66
|
-
stderr_lines += 1
|
67
|
-
|
68
|
-
stdout_thread = threading.Thread(
|
69
|
-
target=stream_output,
|
70
|
-
args=(process.stdout, stdout_file, self.report_stdout, "stdout"),
|
55
|
+
stdout_lines, stderr_lines = self._stream_process_output(
|
56
|
+
process, stdout_file, stderr_file
|
71
57
|
)
|
72
|
-
|
73
|
-
|
74
|
-
args=(process.stderr, stderr_file, self.report_stderr, "stderr"),
|
75
|
-
)
|
76
|
-
stdout_thread.start()
|
77
|
-
stderr_thread.start()
|
78
|
-
try:
|
79
|
-
return_code = process.wait(timeout=timeout)
|
80
|
-
except subprocess.TimeoutExpired:
|
81
|
-
process.kill()
|
82
|
-
self.report_error(
|
83
|
-
tr("\u274c Timed out after {timeout} seconds.", timeout=timeout)
|
84
|
-
)
|
58
|
+
return_code = self._wait_for_process(process, timeout)
|
59
|
+
if return_code is None:
|
85
60
|
return tr(
|
86
61
|
"Code timed out after {timeout} seconds.", timeout=timeout
|
87
62
|
)
|
88
|
-
stdout_thread.join()
|
89
|
-
stderr_thread.join()
|
90
63
|
stdout_file.flush()
|
91
64
|
stderr_file.flush()
|
92
|
-
|
93
65
|
self.report_success(
|
94
66
|
tr("\u2705 Return code {return_code}", return_code=return_code)
|
95
67
|
)
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
) as out_f:
|
100
|
-
stdout_content = out_f.read()
|
101
|
-
with open(
|
102
|
-
stderr_file.name, "r", encoding="utf-8", errors="replace"
|
103
|
-
) as err_f:
|
104
|
-
stderr_content = err_f.read()
|
105
|
-
max_lines = 100
|
106
|
-
stdout_lines = stdout_content.count("\n")
|
107
|
-
stderr_lines = stderr_content.count("\n")
|
108
|
-
|
109
|
-
def head_tail(text, n=10):
|
110
|
-
lines = text.splitlines()
|
111
|
-
if len(lines) <= 2 * n:
|
112
|
-
return "\n".join(lines)
|
113
|
-
return "\n".join(
|
114
|
-
lines[:n]
|
115
|
-
+ ["... ({} lines omitted) ...".format(len(lines) - 2 * n)]
|
116
|
-
+ lines[-n:]
|
117
|
-
)
|
118
|
-
|
119
|
-
if stdout_lines <= max_lines and stderr_lines <= max_lines:
|
120
|
-
result = (
|
121
|
-
f"Return code: {return_code}\n--- STDOUT ---\n{stdout_content}"
|
122
|
-
)
|
123
|
-
if stderr_content.strip():
|
124
|
-
result += f"\n--- STDERR ---\n{stderr_content}"
|
125
|
-
return result
|
126
|
-
else:
|
127
|
-
result = (
|
128
|
-
f"stdout_file: {stdout_file.name} (lines: {stdout_lines})\n"
|
129
|
-
)
|
130
|
-
if stderr_lines > 0 and stderr_content.strip():
|
131
|
-
result += (
|
132
|
-
f"stderr_file: {stderr_file.name} (lines: {stderr_lines})\n"
|
133
|
-
)
|
134
|
-
result += f"returncode: {return_code}\n"
|
135
|
-
result += (
|
136
|
-
"--- STDOUT (head/tail) ---\n"
|
137
|
-
+ head_tail(stdout_content)
|
138
|
-
+ "\n"
|
139
|
-
)
|
140
|
-
if stderr_content.strip():
|
141
|
-
result += (
|
142
|
-
"--- STDERR (head/tail) ---\n"
|
143
|
-
+ head_tail(stderr_content)
|
144
|
-
+ "\n"
|
145
|
-
)
|
146
|
-
result += "Use the get_lines tool to inspect the contents of these files when needed."
|
147
|
-
return result
|
68
|
+
return self._format_result(
|
69
|
+
stdout_file.name, stderr_file.name, return_code
|
70
|
+
)
|
148
71
|
except Exception as e:
|
149
72
|
self.report_error(tr("\u274c Error: {error}", error=e))
|
150
73
|
return tr("Error running code: {error}", error=e)
|
74
|
+
|
75
|
+
def _stream_process_output(self, process, stdout_file, stderr_file):
|
76
|
+
stdout_lines = 0
|
77
|
+
stderr_lines = 0
|
78
|
+
|
79
|
+
def stream_output(stream, file_obj, report_func, count_func):
|
80
|
+
nonlocal stdout_lines, stderr_lines
|
81
|
+
for line in stream:
|
82
|
+
file_obj.write(line)
|
83
|
+
file_obj.flush()
|
84
|
+
report_func(line)
|
85
|
+
if count_func == "stdout":
|
86
|
+
stdout_lines += 1
|
87
|
+
else:
|
88
|
+
stderr_lines += 1
|
89
|
+
|
90
|
+
stdout_thread = threading.Thread(
|
91
|
+
target=stream_output,
|
92
|
+
args=(process.stdout, stdout_file, self.report_stdout, "stdout"),
|
93
|
+
)
|
94
|
+
stderr_thread = threading.Thread(
|
95
|
+
target=stream_output,
|
96
|
+
args=(process.stderr, stderr_file, self.report_stderr, "stderr"),
|
97
|
+
)
|
98
|
+
stdout_thread.start()
|
99
|
+
stderr_thread.start()
|
100
|
+
stdout_thread.join()
|
101
|
+
stderr_thread.join()
|
102
|
+
return stdout_lines, stderr_lines
|
103
|
+
|
104
|
+
def _wait_for_process(self, process, timeout):
|
105
|
+
try:
|
106
|
+
return process.wait(timeout=timeout)
|
107
|
+
except subprocess.TimeoutExpired:
|
108
|
+
process.kill()
|
109
|
+
self.report_error(
|
110
|
+
tr("\u274c Timed out after {timeout} seconds.", timeout=timeout)
|
111
|
+
)
|
112
|
+
return None
|
113
|
+
|
114
|
+
def _format_result(self, stdout_file_name, stderr_file_name, return_code):
|
115
|
+
with open(stdout_file_name, "r", encoding="utf-8", errors="replace") as out_f:
|
116
|
+
stdout_content = out_f.read()
|
117
|
+
with open(stderr_file_name, "r", encoding="utf-8", errors="replace") as err_f:
|
118
|
+
stderr_content = err_f.read()
|
119
|
+
max_lines = 100
|
120
|
+
stdout_lines = stdout_content.count("\n")
|
121
|
+
stderr_lines = stderr_content.count("\n")
|
122
|
+
|
123
|
+
def head_tail(text, n=10):
|
124
|
+
lines = text.splitlines()
|
125
|
+
if len(lines) <= 2 * n:
|
126
|
+
return "\n".join(lines)
|
127
|
+
return "\n".join(
|
128
|
+
lines[:n]
|
129
|
+
+ ["... ({} lines omitted) ...".format(len(lines) - 2 * n)]
|
130
|
+
+ lines[-n:]
|
131
|
+
)
|
132
|
+
|
133
|
+
if stdout_lines <= max_lines and stderr_lines <= max_lines:
|
134
|
+
result = f"Return code: {return_code}\n--- STDOUT ---\n{stdout_content}"
|
135
|
+
if stderr_content.strip():
|
136
|
+
result += f"\n--- STDERR ---\n{stderr_content}"
|
137
|
+
return result
|
138
|
+
else:
|
139
|
+
result = f"stdout_file: {stdout_file_name} (lines: {stdout_lines})\n"
|
140
|
+
if stderr_lines > 0 and stderr_content.strip():
|
141
|
+
result += f"stderr_file: {stderr_file_name} (lines: {stderr_lines})\n"
|
142
|
+
result += f"returncode: {return_code}\n"
|
143
|
+
result += "--- STDOUT (head/tail) ---\n" + head_tail(stdout_content) + "\n"
|
144
|
+
if stderr_content.strip():
|
145
|
+
result += (
|
146
|
+
"--- STDERR (head/tail) ---\n" + head_tail(stderr_content) + "\n"
|
147
|
+
)
|
148
|
+
result += "Use the get_lines tool to inspect the contents of these files when needed."
|
149
|
+
return result
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import subprocess
|
2
|
+
import os
|
2
3
|
import sys
|
3
4
|
import tempfile
|
4
5
|
import threading
|
@@ -47,102 +48,100 @@ class PythonFileRunnerTool(ToolBase):
|
|
47
48
|
bufsize=1,
|
48
49
|
universal_newlines=True,
|
49
50
|
encoding="utf-8",
|
50
|
-
env={**
|
51
|
+
env={**os.environ, "PYTHONIOENCODING": "utf-8"},
|
51
52
|
)
|
52
|
-
stdout_lines =
|
53
|
-
|
54
|
-
|
55
|
-
def stream_output(stream, file_obj, report_func, count_func):
|
56
|
-
nonlocal stdout_lines, stderr_lines
|
57
|
-
for line in stream:
|
58
|
-
file_obj.write(line)
|
59
|
-
file_obj.flush()
|
60
|
-
report_func(line)
|
61
|
-
if count_func == "stdout":
|
62
|
-
stdout_lines += 1
|
63
|
-
else:
|
64
|
-
stderr_lines += 1
|
65
|
-
|
66
|
-
stdout_thread = threading.Thread(
|
67
|
-
target=stream_output,
|
68
|
-
args=(process.stdout, stdout_file, self.report_stdout, "stdout"),
|
53
|
+
stdout_lines, stderr_lines = self._stream_process_output(
|
54
|
+
process, stdout_file, stderr_file
|
69
55
|
)
|
70
|
-
|
71
|
-
|
72
|
-
args=(process.stderr, stderr_file, self.report_stderr, "stderr"),
|
73
|
-
)
|
74
|
-
stdout_thread.start()
|
75
|
-
stderr_thread.start()
|
76
|
-
try:
|
77
|
-
return_code = process.wait(timeout=timeout)
|
78
|
-
except subprocess.TimeoutExpired:
|
79
|
-
process.kill()
|
80
|
-
self.report_error(
|
81
|
-
tr("\u274c Timed out after {timeout} seconds.", timeout=timeout)
|
82
|
-
)
|
56
|
+
return_code = self._wait_for_process(process, timeout)
|
57
|
+
if return_code is None:
|
83
58
|
return tr(
|
84
59
|
"Code timed out after {timeout} seconds.", timeout=timeout
|
85
60
|
)
|
86
|
-
stdout_thread.join()
|
87
|
-
stderr_thread.join()
|
88
61
|
stdout_file.flush()
|
89
62
|
stderr_file.flush()
|
90
|
-
|
91
63
|
self.report_success(
|
92
64
|
tr("\u2705 Return code {return_code}", return_code=return_code)
|
93
65
|
)
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
) as out_f:
|
98
|
-
stdout_content = out_f.read()
|
99
|
-
with open(
|
100
|
-
stderr_file.name, "r", encoding="utf-8", errors="replace"
|
101
|
-
) as err_f:
|
102
|
-
stderr_content = err_f.read()
|
103
|
-
max_lines = 100
|
104
|
-
stdout_lines = stdout_content.count("\n")
|
105
|
-
stderr_lines = stderr_content.count("\n")
|
106
|
-
|
107
|
-
def head_tail(text, n=10):
|
108
|
-
lines = text.splitlines()
|
109
|
-
if len(lines) <= 2 * n:
|
110
|
-
return "\n".join(lines)
|
111
|
-
return "\n".join(
|
112
|
-
lines[:n]
|
113
|
-
+ ["... ({} lines omitted) ...".format(len(lines) - 2 * n)]
|
114
|
-
+ lines[-n:]
|
115
|
-
)
|
116
|
-
|
117
|
-
if stdout_lines <= max_lines and stderr_lines <= max_lines:
|
118
|
-
result = (
|
119
|
-
f"Return code: {return_code}\n--- STDOUT ---\n{stdout_content}"
|
120
|
-
)
|
121
|
-
if stderr_content.strip():
|
122
|
-
result += f"\n--- STDERR ---\n{stderr_content}"
|
123
|
-
return result
|
124
|
-
else:
|
125
|
-
result = (
|
126
|
-
f"stdout_file: {stdout_file.name} (lines: {stdout_lines})\n"
|
127
|
-
)
|
128
|
-
if stderr_lines > 0 and stderr_content.strip():
|
129
|
-
result += (
|
130
|
-
f"stderr_file: {stderr_file.name} (lines: {stderr_lines})\n"
|
131
|
-
)
|
132
|
-
result += f"returncode: {return_code}\n"
|
133
|
-
result += (
|
134
|
-
"--- STDOUT (head/tail) ---\n"
|
135
|
-
+ head_tail(stdout_content)
|
136
|
-
+ "\n"
|
137
|
-
)
|
138
|
-
if stderr_content.strip():
|
139
|
-
result += (
|
140
|
-
"--- STDERR (head/tail) ---\n"
|
141
|
-
+ head_tail(stderr_content)
|
142
|
-
+ "\n"
|
143
|
-
)
|
144
|
-
result += "Use the get_lines tool to inspect the contents of these files when needed."
|
145
|
-
return result
|
66
|
+
return self._format_result(
|
67
|
+
stdout_file.name, stderr_file.name, return_code
|
68
|
+
)
|
146
69
|
except Exception as e:
|
147
70
|
self.report_error(tr("\u274c Error: {error}", error=e))
|
148
71
|
return tr("Error running file: {error}", error=e)
|
72
|
+
|
73
|
+
def _stream_process_output(self, process, stdout_file, stderr_file):
|
74
|
+
stdout_lines = 0
|
75
|
+
stderr_lines = 0
|
76
|
+
|
77
|
+
def stream_output(stream, file_obj, report_func, count_func):
|
78
|
+
nonlocal stdout_lines, stderr_lines
|
79
|
+
for line in stream:
|
80
|
+
file_obj.write(line)
|
81
|
+
file_obj.flush()
|
82
|
+
report_func(line)
|
83
|
+
if count_func == "stdout":
|
84
|
+
stdout_lines += 1
|
85
|
+
else:
|
86
|
+
stderr_lines += 1
|
87
|
+
|
88
|
+
stdout_thread = threading.Thread(
|
89
|
+
target=stream_output,
|
90
|
+
args=(process.stdout, stdout_file, self.report_stdout, "stdout"),
|
91
|
+
)
|
92
|
+
stderr_thread = threading.Thread(
|
93
|
+
target=stream_output,
|
94
|
+
args=(process.stderr, stderr_file, self.report_stderr, "stderr"),
|
95
|
+
)
|
96
|
+
stdout_thread.start()
|
97
|
+
stderr_thread.start()
|
98
|
+
stdout_thread.join()
|
99
|
+
stderr_thread.join()
|
100
|
+
return stdout_lines, stderr_lines
|
101
|
+
|
102
|
+
def _wait_for_process(self, process, timeout):
|
103
|
+
try:
|
104
|
+
return process.wait(timeout=timeout)
|
105
|
+
except subprocess.TimeoutExpired:
|
106
|
+
process.kill()
|
107
|
+
self.report_error(
|
108
|
+
tr("\u274c Timed out after {timeout} seconds.", timeout=timeout)
|
109
|
+
)
|
110
|
+
return None
|
111
|
+
|
112
|
+
def _format_result(self, stdout_file_name, stderr_file_name, return_code):
|
113
|
+
with open(stdout_file_name, "r", encoding="utf-8", errors="replace") as out_f:
|
114
|
+
stdout_content = out_f.read()
|
115
|
+
with open(stderr_file_name, "r", encoding="utf-8", errors="replace") as err_f:
|
116
|
+
stderr_content = err_f.read()
|
117
|
+
max_lines = 100
|
118
|
+
stdout_lines = stdout_content.count("\n")
|
119
|
+
stderr_lines = stderr_content.count("\n")
|
120
|
+
|
121
|
+
def head_tail(text, n=10):
|
122
|
+
lines = text.splitlines()
|
123
|
+
if len(lines) <= 2 * n:
|
124
|
+
return "\n".join(lines)
|
125
|
+
return "\n".join(
|
126
|
+
lines[:n]
|
127
|
+
+ ["... ({} lines omitted) ...".format(len(lines) - 2 * n)]
|
128
|
+
+ lines[-n:]
|
129
|
+
)
|
130
|
+
|
131
|
+
if stdout_lines <= max_lines and stderr_lines <= max_lines:
|
132
|
+
result = f"Return code: {return_code}\n--- STDOUT ---\n{stdout_content}"
|
133
|
+
if stderr_content.strip():
|
134
|
+
result += f"\n--- STDERR ---\n{stderr_content}"
|
135
|
+
return result
|
136
|
+
else:
|
137
|
+
result = f"stdout_file: {stdout_file_name} (lines: {stdout_lines})\n"
|
138
|
+
if stderr_lines > 0 and stderr_content.strip():
|
139
|
+
result += f"stderr_file: {stderr_file_name} (lines: {stderr_lines})\n"
|
140
|
+
result += f"returncode: {return_code}\n"
|
141
|
+
result += "--- STDOUT (head/tail) ---\n" + head_tail(stdout_content) + "\n"
|
142
|
+
if stderr_content.strip():
|
143
|
+
result += (
|
144
|
+
"--- STDERR (head/tail) ---\n" + head_tail(stderr_content) + "\n"
|
145
|
+
)
|
146
|
+
result += "Use the get_lines tool to inspect the contents of these files when needed."
|
147
|
+
return result
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import subprocess
|
2
|
+
import os
|
2
3
|
import sys
|
3
4
|
import tempfile
|
4
5
|
import threading
|
@@ -51,104 +52,102 @@ class PythonStdinRunnerTool(ToolBase):
|
|
51
52
|
bufsize=1,
|
52
53
|
universal_newlines=True,
|
53
54
|
encoding="utf-8",
|
54
|
-
env={**
|
55
|
+
env={**os.environ, "PYTHONIOENCODING": "utf-8"},
|
55
56
|
)
|
56
|
-
stdout_lines =
|
57
|
-
|
58
|
-
|
59
|
-
def stream_output(stream, file_obj, report_func, count_func):
|
60
|
-
nonlocal stdout_lines, stderr_lines
|
61
|
-
for line in stream:
|
62
|
-
file_obj.write(line)
|
63
|
-
file_obj.flush()
|
64
|
-
report_func(line)
|
65
|
-
if count_func == "stdout":
|
66
|
-
stdout_lines += 1
|
67
|
-
else:
|
68
|
-
stderr_lines += 1
|
69
|
-
|
70
|
-
stdout_thread = threading.Thread(
|
71
|
-
target=stream_output,
|
72
|
-
args=(process.stdout, stdout_file, self.report_stdout, "stdout"),
|
57
|
+
stdout_lines, stderr_lines = self._stream_process_output(
|
58
|
+
process, stdout_file, stderr_file, code
|
73
59
|
)
|
74
|
-
|
75
|
-
|
76
|
-
args=(process.stderr, stderr_file, self.report_stderr, "stderr"),
|
77
|
-
)
|
78
|
-
stdout_thread.start()
|
79
|
-
stderr_thread.start()
|
80
|
-
try:
|
81
|
-
process.stdin.write(code)
|
82
|
-
process.stdin.close()
|
83
|
-
return_code = process.wait(timeout=timeout)
|
84
|
-
except subprocess.TimeoutExpired:
|
85
|
-
process.kill()
|
86
|
-
self.report_error(
|
87
|
-
tr("\u274c Timed out after {timeout} seconds.", timeout=timeout)
|
88
|
-
)
|
60
|
+
return_code = self._wait_for_process(process, timeout)
|
61
|
+
if return_code is None:
|
89
62
|
return tr(
|
90
63
|
"Code timed out after {timeout} seconds.", timeout=timeout
|
91
64
|
)
|
92
|
-
stdout_thread.join()
|
93
|
-
stderr_thread.join()
|
94
65
|
stdout_file.flush()
|
95
66
|
stderr_file.flush()
|
96
|
-
|
97
67
|
self.report_success(
|
98
68
|
tr("\u2705 Return code {return_code}", return_code=return_code)
|
99
69
|
)
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
) as out_f:
|
104
|
-
stdout_content = out_f.read()
|
105
|
-
with open(
|
106
|
-
stderr_file.name, "r", encoding="utf-8", errors="replace"
|
107
|
-
) as err_f:
|
108
|
-
stderr_content = err_f.read()
|
109
|
-
max_lines = 100
|
110
|
-
stdout_lines = stdout_content.count("\n")
|
111
|
-
stderr_lines = stderr_content.count("\n")
|
112
|
-
|
113
|
-
def head_tail(text, n=10):
|
114
|
-
lines = text.splitlines()
|
115
|
-
if len(lines) <= 2 * n:
|
116
|
-
return "\n".join(lines)
|
117
|
-
return "\n".join(
|
118
|
-
lines[:n]
|
119
|
-
+ ["... ({} lines omitted) ...".format(len(lines) - 2 * n)]
|
120
|
-
+ lines[-n:]
|
121
|
-
)
|
122
|
-
|
123
|
-
if stdout_lines <= max_lines and stderr_lines <= max_lines:
|
124
|
-
result = (
|
125
|
-
f"Return code: {return_code}\n--- STDOUT ---\n{stdout_content}"
|
126
|
-
)
|
127
|
-
if stderr_content.strip():
|
128
|
-
result += f"\n--- STDERR ---\n{stderr_content}"
|
129
|
-
return result
|
130
|
-
else:
|
131
|
-
result = (
|
132
|
-
f"stdout_file: {stdout_file.name} (lines: {stdout_lines})\n"
|
133
|
-
)
|
134
|
-
if stderr_lines > 0 and stderr_content.strip():
|
135
|
-
result += (
|
136
|
-
f"stderr_file: {stderr_file.name} (lines: {stderr_lines})\n"
|
137
|
-
)
|
138
|
-
result += f"returncode: {return_code}\n"
|
139
|
-
result += (
|
140
|
-
"--- STDOUT (head/tail) ---\n"
|
141
|
-
+ head_tail(stdout_content)
|
142
|
-
+ "\n"
|
143
|
-
)
|
144
|
-
if stderr_content.strip():
|
145
|
-
result += (
|
146
|
-
"--- STDERR (head/tail) ---\n"
|
147
|
-
+ head_tail(stderr_content)
|
148
|
-
+ "\n"
|
149
|
-
)
|
150
|
-
result += "Use the get_lines tool to inspect the contents of these files when needed."
|
151
|
-
return result
|
70
|
+
return self._format_result(
|
71
|
+
stdout_file.name, stderr_file.name, return_code
|
72
|
+
)
|
152
73
|
except Exception as e:
|
153
74
|
self.report_error(tr("\u274c Error: {error}", error=e))
|
154
75
|
return tr("Error running code via stdin: {error}", error=e)
|
76
|
+
|
77
|
+
def _stream_process_output(self, process, stdout_file, stderr_file, code):
|
78
|
+
stdout_lines = 0
|
79
|
+
stderr_lines = 0
|
80
|
+
|
81
|
+
def stream_output(stream, file_obj, report_func, count_func):
|
82
|
+
nonlocal stdout_lines, stderr_lines
|
83
|
+
for line in stream:
|
84
|
+
file_obj.write(line)
|
85
|
+
file_obj.flush()
|
86
|
+
report_func(line)
|
87
|
+
if count_func == "stdout":
|
88
|
+
stdout_lines += 1
|
89
|
+
else:
|
90
|
+
stderr_lines += 1
|
91
|
+
|
92
|
+
stdout_thread = threading.Thread(
|
93
|
+
target=stream_output,
|
94
|
+
args=(process.stdout, stdout_file, self.report_stdout, "stdout"),
|
95
|
+
)
|
96
|
+
stderr_thread = threading.Thread(
|
97
|
+
target=stream_output,
|
98
|
+
args=(process.stderr, stderr_file, self.report_stderr, "stderr"),
|
99
|
+
)
|
100
|
+
stdout_thread.start()
|
101
|
+
stderr_thread.start()
|
102
|
+
process.stdin.write(code)
|
103
|
+
process.stdin.close()
|
104
|
+
stdout_thread.join()
|
105
|
+
stderr_thread.join()
|
106
|
+
return stdout_lines, stderr_lines
|
107
|
+
|
108
|
+
def _wait_for_process(self, process, timeout):
|
109
|
+
try:
|
110
|
+
return process.wait(timeout=timeout)
|
111
|
+
except subprocess.TimeoutExpired:
|
112
|
+
process.kill()
|
113
|
+
self.report_error(
|
114
|
+
tr("\u274c Timed out after {timeout} seconds.", timeout=timeout)
|
115
|
+
)
|
116
|
+
return None
|
117
|
+
|
118
|
+
def _format_result(self, stdout_file_name, stderr_file_name, return_code):
|
119
|
+
with open(stdout_file_name, "r", encoding="utf-8", errors="replace") as out_f:
|
120
|
+
stdout_content = out_f.read()
|
121
|
+
with open(stderr_file_name, "r", encoding="utf-8", errors="replace") as err_f:
|
122
|
+
stderr_content = err_f.read()
|
123
|
+
max_lines = 100
|
124
|
+
stdout_lines = stdout_content.count("\n")
|
125
|
+
stderr_lines = stderr_content.count("\n")
|
126
|
+
|
127
|
+
def head_tail(text, n=10):
|
128
|
+
lines = text.splitlines()
|
129
|
+
if len(lines) <= 2 * n:
|
130
|
+
return "\n".join(lines)
|
131
|
+
return "\n".join(
|
132
|
+
lines[:n]
|
133
|
+
+ ["... ({} lines omitted) ...".format(len(lines) - 2 * n)]
|
134
|
+
+ lines[-n:]
|
135
|
+
)
|
136
|
+
|
137
|
+
if stdout_lines <= max_lines and stderr_lines <= max_lines:
|
138
|
+
result = f"Return code: {return_code}\n--- STDOUT ---\n{stdout_content}"
|
139
|
+
if stderr_content.strip():
|
140
|
+
result += f"\n--- STDERR ---\n{stderr_content}"
|
141
|
+
return result
|
142
|
+
else:
|
143
|
+
result = f"stdout_file: {stdout_file_name} (lines: {stdout_lines})\n"
|
144
|
+
if stderr_lines > 0 and stderr_content.strip():
|
145
|
+
result += f"stderr_file: {stderr_file_name} (lines: {stderr_lines})\n"
|
146
|
+
result += f"returncode: {return_code}\n"
|
147
|
+
result += "--- STDOUT (head/tail) ---\n" + head_tail(stdout_content) + "\n"
|
148
|
+
if stderr_content.strip():
|
149
|
+
result += (
|
150
|
+
"--- STDERR (head/tail) ---\n" + head_tail(stderr_content) + "\n"
|
151
|
+
)
|
152
|
+
result += "Use the get_lines tool to inspect the contents of these files when needed."
|
153
|
+
return result
|
@@ -26,7 +26,7 @@ class RemoveDirectoryTool(ToolBase):
|
|
26
26
|
disp_path = display_path(file_path)
|
27
27
|
self.report_info(
|
28
28
|
ActionType.WRITE,
|
29
|
-
tr("🗃️
|
29
|
+
tr("🗃️ Remove directory '{disp_path}' ...", disp_path=disp_path),
|
30
30
|
)
|
31
31
|
backup_zip = None
|
32
32
|
try:
|
@@ -31,7 +31,7 @@ class RemoveFileTool(ToolBase):
|
|
31
31
|
# Report initial info about what is going to be removed
|
32
32
|
self.report_info(
|
33
33
|
ActionType.WRITE,
|
34
|
-
tr("🗑️
|
34
|
+
tr("🗑️ Remove file '{disp_path}' ...", disp_path=disp_path),
|
35
35
|
)
|
36
36
|
if not os.path.exists(path):
|
37
37
|
self.report_error(tr("❌ File does not exist."))
|
@@ -39,7 +39,7 @@ class ReplaceFileTool(ToolBase):
|
|
39
39
|
if not tracker.last_operation_is_full_read_or_replace(file_path):
|
40
40
|
self.report_info(
|
41
41
|
ActionType.WRITE,
|
42
|
-
tr("📝
|
42
|
+
tr("📝 Replace file '{disp_path}' ...", disp_path=disp_path),
|
43
43
|
)
|
44
44
|
self.report_warning(tr("ℹ️ Missing full view."))
|
45
45
|
try:
|
@@ -54,7 +54,7 @@ class ReplaceFileTool(ToolBase):
|
|
54
54
|
)
|
55
55
|
self.report_info(
|
56
56
|
ActionType.WRITE,
|
57
|
-
tr("📝
|
57
|
+
tr("📝 Replace file '{disp_path}' ...", disp_path=disp_path),
|
58
58
|
)
|
59
59
|
backup_path = file_path + ".bak"
|
60
60
|
shutil.copy2(file_path, backup_path)
|