janito 0.12.0__py3-none-any.whl → 0.14.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- janito/__init__.py +1 -1
- janito/cli/agent/__init__.py +7 -0
- janito/cli/agent/conversation.py +149 -0
- janito/cli/agent/initialization.py +172 -0
- janito/cli/agent/query.py +108 -0
- janito/cli/agent.py +7 -282
- janito/cli/app.py +105 -9
- janito/cli/commands/__init__.py +12 -0
- janito/cli/commands/config.py +242 -0
- janito/cli/commands/history.py +119 -0
- janito/cli/commands/profile.py +72 -0
- janito/cli/commands/validation.py +24 -0
- janito/cli/commands/workspace.py +31 -0
- janito/cli/commands.py +9 -326
- janito/config.py +37 -0
- janito/data/instructions_template.txt +9 -5
- janito/tools/__init__.py +8 -2
- janito/tools/bash/bash.py +3 -1
- janito/tools/bash/unix_persistent_bash.py +183 -181
- janito/tools/bash/win_persistent_bash.py +4 -2
- janito/tools/fetch_webpage/__init__.py +22 -33
- janito/tools/fetch_webpage/core.py +182 -155
- janito/tools/rich_console.py +46 -9
- janito/tools/search_text.py +225 -238
- janito/tools/str_replace_editor/handlers/str_replace.py +3 -1
- janito/tools/str_replace_editor/handlers/view.py +14 -8
- janito/tools/think.py +37 -0
- janito/tools/usage_tracker.py +1 -0
- janito-0.14.0.dist-info/METADATA +396 -0
- janito-0.14.0.dist-info/RECORD +53 -0
- janito/test_file.py +0 -4
- janito/tools/fetch_webpage/chunking.py +0 -76
- janito/tools/fetch_webpage/extractors.py +0 -276
- janito/tools/fetch_webpage/news.py +0 -137
- janito/tools/fetch_webpage/utils.py +0 -108
- janito-0.12.0.dist-info/METADATA +0 -203
- janito-0.12.0.dist-info/RECORD +0 -47
- {janito-0.12.0.dist-info → janito-0.14.0.dist-info}/WHEEL +0 -0
- {janito-0.12.0.dist-info → janito-0.14.0.dist-info}/entry_points.txt +0 -0
- {janito-0.12.0.dist-info → janito-0.14.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,182 +1,184 @@
|
|
1
|
-
import subprocess
|
2
|
-
import time
|
3
|
-
import uuid
|
4
|
-
|
5
|
-
class PersistentBash:
|
6
|
-
"""
|
7
|
-
A wrapper class that maintains a persistent Bash session.
|
8
|
-
Allows sending commands and collecting output without restarting Bash.
|
9
|
-
"""
|
10
|
-
|
11
|
-
def __init__(self, bash_path=None):
|
12
|
-
"""
|
13
|
-
Initialize a persistent Bash session.
|
14
|
-
|
15
|
-
Args:
|
16
|
-
bash_path (str, optional): Path to the Bash executable. If None, tries to detect automatically.
|
17
|
-
"""
|
18
|
-
self.process = None
|
19
|
-
self.bash_path = bash_path
|
20
|
-
|
21
|
-
# If bash_path is not provided, try to detect it
|
22
|
-
if self.bash_path is None:
|
23
|
-
# On Unix-like systems, bash is usually in the PATH
|
24
|
-
self.bash_path = "bash"
|
25
|
-
|
26
|
-
# Check if bash exists
|
27
|
-
try:
|
28
|
-
subprocess.run(["which", "bash"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
29
|
-
except subprocess.CalledProcessError as err:
|
30
|
-
raise FileNotFoundError("Could not find bash executable. Please specify the path manually.") from err
|
31
|
-
|
32
|
-
# Start the bash process
|
33
|
-
self.start_process()
|
34
|
-
|
35
|
-
def start_process(self):
|
36
|
-
"""Start the Bash process."""
|
37
|
-
# Create a subprocess with pipe for stdin, stdout, and stderr
|
38
|
-
bash_args = [self.bash_path]
|
39
|
-
|
40
|
-
self.process = subprocess.Popen(
|
41
|
-
bash_args,
|
42
|
-
stdin=subprocess.PIPE,
|
43
|
-
stdout=subprocess.PIPE,
|
44
|
-
stderr=subprocess.STDOUT, # Redirect stderr to stdout
|
45
|
-
text=True, # Use text mode for input/output
|
46
|
-
bufsize=0, # Unbuffered
|
47
|
-
universal_newlines=True, # Universal newlines mode
|
48
|
-
)
|
49
|
-
|
50
|
-
# Set up a more reliable environment
|
51
|
-
setup_commands = [
|
52
|
-
"export PS1='$ '", # Simple prompt to avoid parsing issues
|
53
|
-
"export TERM=dumb", # Disable color codes and other terminal features
|
54
|
-
"set +o history", # Disable history
|
55
|
-
"shopt -s expand_aliases", # Enable alias expansion
|
56
|
-
]
|
57
|
-
|
58
|
-
# Send setup commands
|
59
|
-
for cmd in setup_commands:
|
60
|
-
self._send_command(cmd)
|
61
|
-
|
62
|
-
# Clear initial output with a marker
|
63
|
-
marker = f"INIT_COMPLETE_{uuid.uuid4().hex}"
|
64
|
-
self._send_command(f"echo {marker}")
|
65
|
-
|
66
|
-
while True:
|
67
|
-
line = self.process.stdout.readline().strip()
|
68
|
-
if marker in line:
|
69
|
-
break
|
70
|
-
|
71
|
-
def _send_command(self, command):
|
72
|
-
"""Send a command to the Bash process without reading the output."""
|
73
|
-
if self.process is None or self.process.poll() is not None:
|
74
|
-
self.start_process()
|
75
|
-
|
76
|
-
self.process.stdin.write(command + "\n")
|
77
|
-
self.process.stdin.flush()
|
78
|
-
|
79
|
-
def execute(self, command, timeout=None):
|
80
|
-
"""
|
81
|
-
Execute a command in the Bash session and return the output.
|
82
|
-
|
83
|
-
Args:
|
84
|
-
command (str): The command to execute.
|
85
|
-
timeout (int, optional): Timeout in seconds. If None, no timeout is applied.
|
86
|
-
|
87
|
-
Returns:
|
88
|
-
str: The command output.
|
89
|
-
"""
|
90
|
-
from janito.tools.rich_console import console
|
91
|
-
|
92
|
-
if self.process is None or self.process.poll() is not None:
|
93
|
-
# Process has terminated, restart it
|
94
|
-
self.start_process()
|
95
|
-
|
96
|
-
# Create a unique marker to identify the end of output
|
97
|
-
end_marker = f"END_OF_COMMAND_{uuid.uuid4().hex}"
|
98
|
-
|
99
|
-
# Construct the wrapped command with echo markers
|
100
|
-
# Only use timeout when explicitly requested
|
101
|
-
if timeout is not None and timeout > 0:
|
102
|
-
# Check if timeout command is available
|
103
|
-
is_timeout_available = False
|
104
|
-
try:
|
105
|
-
check_cmd = "command -v timeout > /dev/null 2>&1 && echo available || echo unavailable"
|
106
|
-
self._send_command(check_cmd)
|
107
|
-
for _ in range(10): # Read up to 10 lines to find the result
|
108
|
-
line = self.process.stdout.readline().strip()
|
109
|
-
if "available" in line:
|
110
|
-
is_timeout_available = True
|
111
|
-
break
|
112
|
-
elif "unavailable" in line:
|
113
|
-
is_timeout_available = False
|
114
|
-
break
|
115
|
-
except:
|
116
|
-
is_timeout_available = False
|
117
|
-
|
118
|
-
if is_timeout_available:
|
119
|
-
# For timeout to work with shell syntax, we need to use bash -c
|
120
|
-
escaped_command = command.replace('"', '\\"')
|
121
|
-
wrapped_command = f"timeout {timeout}s bash -c \"{escaped_command}\" 2>&1; echo '{end_marker}'"
|
122
|
-
else:
|
123
|
-
wrapped_command = f"{command} 2>&1; echo '{end_marker}'"
|
124
|
-
else:
|
125
|
-
wrapped_command = f"{command} 2>&1; echo '{end_marker}'"
|
126
|
-
|
127
|
-
# Send the command
|
128
|
-
self._send_command(wrapped_command)
|
129
|
-
|
130
|
-
# Collect output until the end marker is found
|
131
|
-
output_lines = []
|
132
|
-
start_time = time.time()
|
133
|
-
max_wait = timeout if timeout is not None else 3600 # Default to 1 hour if no timeout
|
134
|
-
|
135
|
-
while time.time() - start_time < max_wait + 5: # Add buffer time
|
136
|
-
try:
|
137
|
-
line = self.process.stdout.readline().rstrip('\r\n')
|
138
|
-
if end_marker in line:
|
139
|
-
break
|
140
|
-
|
141
|
-
# Print the output to the console in real-time
|
142
|
-
if line:
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
1
|
+
import subprocess
|
2
|
+
import time
|
3
|
+
import uuid
|
4
|
+
|
5
|
+
class PersistentBash:
|
6
|
+
"""
|
7
|
+
A wrapper class that maintains a persistent Bash session.
|
8
|
+
Allows sending commands and collecting output without restarting Bash.
|
9
|
+
"""
|
10
|
+
|
11
|
+
def __init__(self, bash_path=None):
|
12
|
+
"""
|
13
|
+
Initialize a persistent Bash session.
|
14
|
+
|
15
|
+
Args:
|
16
|
+
bash_path (str, optional): Path to the Bash executable. If None, tries to detect automatically.
|
17
|
+
"""
|
18
|
+
self.process = None
|
19
|
+
self.bash_path = bash_path
|
20
|
+
|
21
|
+
# If bash_path is not provided, try to detect it
|
22
|
+
if self.bash_path is None:
|
23
|
+
# On Unix-like systems, bash is usually in the PATH
|
24
|
+
self.bash_path = "bash"
|
25
|
+
|
26
|
+
# Check if bash exists
|
27
|
+
try:
|
28
|
+
subprocess.run(["which", "bash"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
29
|
+
except subprocess.CalledProcessError as err:
|
30
|
+
raise FileNotFoundError("Could not find bash executable. Please specify the path manually.") from err
|
31
|
+
|
32
|
+
# Start the bash process
|
33
|
+
self.start_process()
|
34
|
+
|
35
|
+
def start_process(self):
|
36
|
+
"""Start the Bash process."""
|
37
|
+
# Create a subprocess with pipe for stdin, stdout, and stderr
|
38
|
+
bash_args = [self.bash_path]
|
39
|
+
|
40
|
+
self.process = subprocess.Popen(
|
41
|
+
bash_args,
|
42
|
+
stdin=subprocess.PIPE,
|
43
|
+
stdout=subprocess.PIPE,
|
44
|
+
stderr=subprocess.STDOUT, # Redirect stderr to stdout
|
45
|
+
text=True, # Use text mode for input/output
|
46
|
+
bufsize=0, # Unbuffered
|
47
|
+
universal_newlines=True, # Universal newlines mode
|
48
|
+
)
|
49
|
+
|
50
|
+
# Set up a more reliable environment
|
51
|
+
setup_commands = [
|
52
|
+
"export PS1='$ '", # Simple prompt to avoid parsing issues
|
53
|
+
"export TERM=dumb", # Disable color codes and other terminal features
|
54
|
+
"set +o history", # Disable history
|
55
|
+
"shopt -s expand_aliases", # Enable alias expansion
|
56
|
+
]
|
57
|
+
|
58
|
+
# Send setup commands
|
59
|
+
for cmd in setup_commands:
|
60
|
+
self._send_command(cmd)
|
61
|
+
|
62
|
+
# Clear initial output with a marker
|
63
|
+
marker = f"INIT_COMPLETE_{uuid.uuid4().hex}"
|
64
|
+
self._send_command(f"echo {marker}")
|
65
|
+
|
66
|
+
while True:
|
67
|
+
line = self.process.stdout.readline().strip()
|
68
|
+
if marker in line:
|
69
|
+
break
|
70
|
+
|
71
|
+
def _send_command(self, command):
|
72
|
+
"""Send a command to the Bash process without reading the output."""
|
73
|
+
if self.process is None or self.process.poll() is not None:
|
74
|
+
self.start_process()
|
75
|
+
|
76
|
+
self.process.stdin.write(command + "\n")
|
77
|
+
self.process.stdin.flush()
|
78
|
+
|
79
|
+
def execute(self, command, timeout=None):
|
80
|
+
"""
|
81
|
+
Execute a command in the Bash session and return the output.
|
82
|
+
|
83
|
+
Args:
|
84
|
+
command (str): The command to execute.
|
85
|
+
timeout (int, optional): Timeout in seconds. If None, no timeout is applied.
|
86
|
+
|
87
|
+
Returns:
|
88
|
+
str: The command output.
|
89
|
+
"""
|
90
|
+
from janito.tools.rich_console import console
|
91
|
+
|
92
|
+
if self.process is None or self.process.poll() is not None:
|
93
|
+
# Process has terminated, restart it
|
94
|
+
self.start_process()
|
95
|
+
|
96
|
+
# Create a unique marker to identify the end of output
|
97
|
+
end_marker = f"END_OF_COMMAND_{uuid.uuid4().hex}"
|
98
|
+
|
99
|
+
# Construct the wrapped command with echo markers
|
100
|
+
# Only use timeout when explicitly requested
|
101
|
+
if timeout is not None and timeout > 0:
|
102
|
+
# Check if timeout command is available
|
103
|
+
is_timeout_available = False
|
104
|
+
try:
|
105
|
+
check_cmd = "command -v timeout > /dev/null 2>&1 && echo available || echo unavailable"
|
106
|
+
self._send_command(check_cmd)
|
107
|
+
for _ in range(10): # Read up to 10 lines to find the result
|
108
|
+
line = self.process.stdout.readline().strip()
|
109
|
+
if "available" in line:
|
110
|
+
is_timeout_available = True
|
111
|
+
break
|
112
|
+
elif "unavailable" in line:
|
113
|
+
is_timeout_available = False
|
114
|
+
break
|
115
|
+
except:
|
116
|
+
is_timeout_available = False
|
117
|
+
|
118
|
+
if is_timeout_available:
|
119
|
+
# For timeout to work with shell syntax, we need to use bash -c
|
120
|
+
escaped_command = command.replace('"', '\\"')
|
121
|
+
wrapped_command = f"timeout {timeout}s bash -c \"{escaped_command}\" 2>&1; echo '{end_marker}'"
|
122
|
+
else:
|
123
|
+
wrapped_command = f"{command} 2>&1; echo '{end_marker}'"
|
124
|
+
else:
|
125
|
+
wrapped_command = f"{command} 2>&1; echo '{end_marker}'"
|
126
|
+
|
127
|
+
# Send the command
|
128
|
+
self._send_command(wrapped_command)
|
129
|
+
|
130
|
+
# Collect output until the end marker is found
|
131
|
+
output_lines = []
|
132
|
+
start_time = time.time()
|
133
|
+
max_wait = timeout if timeout is not None else 3600 # Default to 1 hour if no timeout
|
134
|
+
|
135
|
+
while time.time() - start_time < max_wait + 5: # Add buffer time
|
136
|
+
try:
|
137
|
+
line = self.process.stdout.readline().rstrip('\r\n')
|
138
|
+
if end_marker in line:
|
139
|
+
break
|
140
|
+
|
141
|
+
# Print the output to the console in real-time if not in trust mode
|
142
|
+
if line:
|
143
|
+
from janito.config import get_config
|
144
|
+
if not get_config().trust_mode:
|
145
|
+
console.print(line)
|
146
|
+
|
147
|
+
output_lines.append(line)
|
148
|
+
except Exception as e:
|
149
|
+
error_msg = f"[Error reading output: {str(e)}]"
|
150
|
+
console.print(error_msg, style="red")
|
151
|
+
output_lines.append(error_msg)
|
152
|
+
continue
|
153
|
+
|
154
|
+
# Check for timeout
|
155
|
+
if time.time() - start_time >= max_wait + 5:
|
156
|
+
timeout_msg = f"Error: Command timed out after {max_wait} seconds"
|
157
|
+
console.print(timeout_msg, style="red bold")
|
158
|
+
output_lines.append(timeout_msg)
|
159
|
+
|
160
|
+
# Try to reset the bash session after a timeout
|
161
|
+
self.close()
|
162
|
+
self.start_process()
|
163
|
+
|
164
|
+
return "\n".join(output_lines)
|
165
|
+
|
166
|
+
def close(self):
|
167
|
+
"""Close the Bash session."""
|
168
|
+
if self.process and self.process.poll() is None:
|
169
|
+
try:
|
170
|
+
self._send_command("exit")
|
171
|
+
self.process.wait(timeout=2)
|
172
|
+
except:
|
173
|
+
pass
|
174
|
+
finally:
|
175
|
+
try:
|
176
|
+
self.process.terminate()
|
177
|
+
except:
|
178
|
+
pass
|
179
|
+
|
180
|
+
self.process = None
|
181
|
+
|
182
|
+
def __del__(self):
|
183
|
+
"""Destructor to ensure the process is closed."""
|
182
184
|
self.close()
|
@@ -222,9 +222,11 @@ class PersistentBash:
|
|
222
222
|
if end_marker in line:
|
223
223
|
break
|
224
224
|
|
225
|
-
# Print the output to the console in real-time
|
225
|
+
# Print the output to the console in real-time if not in trust mode
|
226
226
|
if line:
|
227
|
-
|
227
|
+
from janito.config import get_config
|
228
|
+
if not get_config().trust_mode:
|
229
|
+
console.print(line)
|
228
230
|
|
229
231
|
output_lines.append(line)
|
230
232
|
except UnicodeDecodeError as e:
|
@@ -1,34 +1,23 @@
|
|
1
|
-
"""
|
2
|
-
Webpage Content Extractor Package
|
3
|
-
|
4
|
-
A
|
5
|
-
for processing with LLMs. Features include:
|
6
|
-
-
|
7
|
-
-
|
8
|
-
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
from janito.tools.fetch_webpage.extractors import extract_clean_text, extract_targeted_content, extract_structured_content
|
24
|
-
from janito.tools.fetch_webpage.chunking import chunk_large_content
|
25
|
-
|
26
|
-
__all__ = [
|
27
|
-
'fetch_webpage',
|
28
|
-
'fetch_and_extract',
|
29
|
-
'fetch_and_extract_news_aggregator',
|
30
|
-
'extract_clean_text',
|
31
|
-
'extract_targeted_content',
|
32
|
-
'extract_structured_content',
|
33
|
-
'chunk_large_content'
|
1
|
+
"""
|
2
|
+
Webpage Content Extractor Package
|
3
|
+
|
4
|
+
A simplified tool for extracting clean, relevant content from web pages
|
5
|
+
for processing with LLMs. Features include:
|
6
|
+
- Streamlined content extraction using BeautifulSoup
|
7
|
+
- Clean HTML text extraction
|
8
|
+
- Efficient content chunking
|
9
|
+
|
10
|
+
Dependencies:
|
11
|
+
- requests
|
12
|
+
- beautifulsoup4
|
13
|
+
|
14
|
+
Author: Claude (Anthropic)
|
15
|
+
"""
|
16
|
+
|
17
|
+
from janito.tools.fetch_webpage.core import fetch_webpage, fetch_and_extract, chunk_content
|
18
|
+
|
19
|
+
__all__ = [
|
20
|
+
'fetch_webpage',
|
21
|
+
'fetch_and_extract',
|
22
|
+
'chunk_content'
|
34
23
|
]
|