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.
Files changed (40) hide show
  1. janito/__init__.py +1 -1
  2. janito/cli/agent/__init__.py +7 -0
  3. janito/cli/agent/conversation.py +149 -0
  4. janito/cli/agent/initialization.py +172 -0
  5. janito/cli/agent/query.py +108 -0
  6. janito/cli/agent.py +7 -282
  7. janito/cli/app.py +105 -9
  8. janito/cli/commands/__init__.py +12 -0
  9. janito/cli/commands/config.py +242 -0
  10. janito/cli/commands/history.py +119 -0
  11. janito/cli/commands/profile.py +72 -0
  12. janito/cli/commands/validation.py +24 -0
  13. janito/cli/commands/workspace.py +31 -0
  14. janito/cli/commands.py +9 -326
  15. janito/config.py +37 -0
  16. janito/data/instructions_template.txt +9 -5
  17. janito/tools/__init__.py +8 -2
  18. janito/tools/bash/bash.py +3 -1
  19. janito/tools/bash/unix_persistent_bash.py +183 -181
  20. janito/tools/bash/win_persistent_bash.py +4 -2
  21. janito/tools/fetch_webpage/__init__.py +22 -33
  22. janito/tools/fetch_webpage/core.py +182 -155
  23. janito/tools/rich_console.py +46 -9
  24. janito/tools/search_text.py +225 -238
  25. janito/tools/str_replace_editor/handlers/str_replace.py +3 -1
  26. janito/tools/str_replace_editor/handlers/view.py +14 -8
  27. janito/tools/think.py +37 -0
  28. janito/tools/usage_tracker.py +1 -0
  29. janito-0.14.0.dist-info/METADATA +396 -0
  30. janito-0.14.0.dist-info/RECORD +53 -0
  31. janito/test_file.py +0 -4
  32. janito/tools/fetch_webpage/chunking.py +0 -76
  33. janito/tools/fetch_webpage/extractors.py +0 -276
  34. janito/tools/fetch_webpage/news.py +0 -137
  35. janito/tools/fetch_webpage/utils.py +0 -108
  36. janito-0.12.0.dist-info/METADATA +0 -203
  37. janito-0.12.0.dist-info/RECORD +0 -47
  38. {janito-0.12.0.dist-info → janito-0.14.0.dist-info}/WHEEL +0 -0
  39. {janito-0.12.0.dist-info → janito-0.14.0.dist-info}/entry_points.txt +0 -0
  40. {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
- console.print(line)
144
-
145
- output_lines.append(line)
146
- except Exception as e:
147
- error_msg = f"[Error reading output: {str(e)}]"
148
- console.print(error_msg, style="red")
149
- output_lines.append(error_msg)
150
- continue
151
-
152
- # Check for timeout
153
- if time.time() - start_time >= max_wait + 5:
154
- timeout_msg = f"Error: Command timed out after {max_wait} seconds"
155
- console.print(timeout_msg, style="red bold")
156
- output_lines.append(timeout_msg)
157
-
158
- # Try to reset the bash session after a timeout
159
- self.close()
160
- self.start_process()
161
-
162
- return "\n".join(output_lines)
163
-
164
- def close(self):
165
- """Close the Bash session."""
166
- if self.process and self.process.poll() is None:
167
- try:
168
- self._send_command("exit")
169
- self.process.wait(timeout=2)
170
- except:
171
- pass
172
- finally:
173
- try:
174
- self.process.terminate()
175
- except:
176
- pass
177
-
178
- self.process = None
179
-
180
- def __del__(self):
181
- """Destructor to ensure the process is closed."""
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
- console.print(line)
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 comprehensive tool for extracting clean, relevant content from web pages
5
- for processing with LLMs. Features include:
6
- - General content extraction with multiple methods
7
- - Specialized handling for news aggregator sites
8
- - Targeted extraction based on specific search strings
9
- - Chunking for large content
10
- - Structured content extraction
11
-
12
- Dependencies:
13
- - requests
14
- - beautifulsoup4
15
- - trafilatura
16
- - newspaper3k
17
-
18
- Author: Claude (Anthropic)
19
- """
20
-
21
- from janito.tools.fetch_webpage.core import fetch_webpage, fetch_and_extract
22
- from janito.tools.fetch_webpage.news import fetch_and_extract_news_aggregator
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
  ]