janito 3.3.0__py3-none-any.whl → 3.4.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 (214) hide show
  1. janito/cli/cli_commands/list_plugins.py +48 -52
  2. janito/cli/core/getters.py +0 -3
  3. janito/cli/main_cli.py +12 -9
  4. janito/drivers/openai/driver.py +0 -1
  5. janito/drivers/zai/driver.py +0 -1
  6. janito/hello.txt +0 -0
  7. janito/llm/auth_utils.py +5 -14
  8. janito/plugins/__init__.py +7 -18
  9. janito/{plugins_backup_20250825_070018 → plugins}/auto_loader_fixed.py +11 -12
  10. janito/plugins/base.py +152 -57
  11. janito/plugins/builtin.py +1 -15
  12. janito/{plugins_backup_20250825_070018 → plugins}/core_adapter.py +9 -11
  13. janito/plugins/core_loader.py +34 -58
  14. janito/{plugin_system_backup_20250825_070018/core_loader.py → plugins/core_loader_fixed.py} +26 -45
  15. janito/plugins/discovery.py +3 -3
  16. janito/tools/__init__.py +7 -31
  17. janito/tools/adapters/__init__.py +1 -6
  18. janito/tools/adapters/local/__init__.py +70 -7
  19. janito/{plugins_backup_20250825_070018/tools → tools/adapters/local}/ask_user.py +3 -3
  20. janito/{plugins_backup_20250825_070018/tools → tools/adapters/local}/create_file.py +4 -4
  21. janito/{plugins/core/filemanager/tools → tools/adapters/local}/delete_text_in_file.py +0 -1
  22. janito/{plugins_backup_20250825_070018/tools → tools/adapters/local}/fetch_url.py +3 -3
  23. janito/{plugins_backup_20250825_070018/tools → tools/adapters/local}/replace_text_in_file.py +4 -4
  24. janito/{plugins_backup_20250825_070018/tools → tools/adapters/local}/show_image.py +6 -15
  25. janito/{plugins/core/imagedisplay/tools → tools/adapters/local}/show_image_grid.py +5 -13
  26. janito/tools/base.py +1 -31
  27. janito/tools/function_adapter.py +16 -127
  28. janito/tools/tool_base.py +114 -142
  29. janito/tools/tools_schema.py +6 -12
  30. {janito-3.3.0.dist-info → janito-3.4.0.dist-info}/METADATA +2 -1
  31. janito-3.4.0.dist-info/RECORD +264 -0
  32. janito/cli/cli_commands/check_tools.py +0 -212
  33. janito/plugin_system_backup_20250825_070018/__init__.py +0 -10
  34. janito/plugin_system_backup_20250825_070018/base.py +0 -155
  35. janito/plugin_system_backup_20250825_070018/core_loader_fixed.py +0 -149
  36. janito/plugins/__main__.py +0 -85
  37. janito/plugins/core/__init__.py +0 -7
  38. janito/plugins/core/codeanalyzer/__init__.py +0 -43
  39. janito/plugins/core/filemanager/__init__.py +0 -124
  40. janito/plugins/core/filemanager/tools/copy_file.py +0 -111
  41. janito/plugins/core/filemanager/tools/create_directory.py +0 -97
  42. janito/plugins/core/filemanager/tools/create_file.py +0 -111
  43. janito/plugins/core/filemanager/tools/replace_text_in_file.py +0 -270
  44. janito/plugins/core/imagedisplay/__init__.py +0 -14
  45. janito/plugins/core/imagedisplay/plugin.py +0 -51
  46. janito/plugins/core/imagedisplay/tools/__init__.py +0 -1
  47. janito/plugins/core/imagedisplay/tools/show_image.py +0 -83
  48. janito/plugins/core/system/__init__.py +0 -23
  49. janito/plugins/dev/__init__.py +0 -7
  50. janito/plugins/dev/pythondev/__init__.py +0 -37
  51. janito/plugins/dev/visualization/__init__.py +0 -23
  52. janito/plugins/example_plugin.py +0 -108
  53. janito/plugins/ui/__init__.py +0 -7
  54. janito/plugins/ui/userinterface/__init__.py +0 -16
  55. janito/plugins/ui/userinterface/tools/ask_user.py +0 -110
  56. janito/plugins/web/__init__.py +0 -7
  57. janito/plugins/web/webtools/__init__.py +0 -33
  58. janito/plugins/web/webtools/tools/fetch_url.py +0 -458
  59. janito/plugins_backup_20250825_070018/__init__.py +0 -36
  60. janito/plugins_backup_20250825_070018/builtin.py +0 -102
  61. janito/plugins_backup_20250825_070018/config.py +0 -84
  62. janito/plugins_backup_20250825_070018/core/__init__.py +0 -7
  63. janito/plugins_backup_20250825_070018/core/codeanalyzer/__init__.py +0 -43
  64. janito/plugins_backup_20250825_070018/core/codeanalyzer/tools/get_file_outline/__init__.py +0 -1
  65. janito/plugins_backup_20250825_070018/core/codeanalyzer/tools/get_file_outline/core.py +0 -122
  66. janito/plugins_backup_20250825_070018/core/codeanalyzer/tools/get_file_outline/java_outline.py +0 -47
  67. janito/plugins_backup_20250825_070018/core/codeanalyzer/tools/get_file_outline/markdown_outline.py +0 -14
  68. janito/plugins_backup_20250825_070018/core/codeanalyzer/tools/get_file_outline/python_outline.py +0 -303
  69. janito/plugins_backup_20250825_070018/core/codeanalyzer/tools/get_file_outline/search_outline.py +0 -36
  70. janito/plugins_backup_20250825_070018/core/codeanalyzer/tools/search_text/__init__.py +0 -1
  71. janito/plugins_backup_20250825_070018/core/codeanalyzer/tools/search_text/core.py +0 -205
  72. janito/plugins_backup_20250825_070018/core/codeanalyzer/tools/search_text/match_lines.py +0 -67
  73. janito/plugins_backup_20250825_070018/core/codeanalyzer/tools/search_text/pattern_utils.py +0 -73
  74. janito/plugins_backup_20250825_070018/core/codeanalyzer/tools/search_text/traverse_directory.py +0 -145
  75. janito/plugins_backup_20250825_070018/core/filemanager/__init__.py +0 -124
  76. janito/plugins_backup_20250825_070018/core/filemanager/tools/create_file.py +0 -87
  77. janito/plugins_backup_20250825_070018/core/filemanager/tools/delete_text_in_file.py +0 -135
  78. janito/plugins_backup_20250825_070018/core/filemanager/tools/find_files.py +0 -143
  79. janito/plugins_backup_20250825_070018/core/filemanager/tools/move_file.py +0 -131
  80. janito/plugins_backup_20250825_070018/core/filemanager/tools/read_files.py +0 -58
  81. janito/plugins_backup_20250825_070018/core/filemanager/tools/remove_directory.py +0 -55
  82. janito/plugins_backup_20250825_070018/core/filemanager/tools/remove_file.py +0 -58
  83. janito/plugins_backup_20250825_070018/core/filemanager/tools/replace_text_in_file.py +0 -270
  84. janito/plugins_backup_20250825_070018/core/filemanager/tools/validate_file_syntax/__init__.py +0 -1
  85. janito/plugins_backup_20250825_070018/core/filemanager/tools/validate_file_syntax/core.py +0 -114
  86. janito/plugins_backup_20250825_070018/core/filemanager/tools/validate_file_syntax/css_validator.py +0 -35
  87. janito/plugins_backup_20250825_070018/core/filemanager/tools/validate_file_syntax/html_validator.py +0 -100
  88. janito/plugins_backup_20250825_070018/core/filemanager/tools/validate_file_syntax/jinja2_validator.py +0 -50
  89. janito/plugins_backup_20250825_070018/core/filemanager/tools/validate_file_syntax/js_validator.py +0 -27
  90. janito/plugins_backup_20250825_070018/core/filemanager/tools/validate_file_syntax/json_validator.py +0 -6
  91. janito/plugins_backup_20250825_070018/core/filemanager/tools/validate_file_syntax/markdown_validator.py +0 -109
  92. janito/plugins_backup_20250825_070018/core/filemanager/tools/validate_file_syntax/ps1_validator.py +0 -32
  93. janito/plugins_backup_20250825_070018/core/filemanager/tools/validate_file_syntax/python_validator.py +0 -5
  94. janito/plugins_backup_20250825_070018/core/filemanager/tools/validate_file_syntax/xml_validator.py +0 -11
  95. janito/plugins_backup_20250825_070018/core/filemanager/tools/validate_file_syntax/yaml_validator.py +0 -6
  96. janito/plugins_backup_20250825_070018/core/filemanager/tools/view_file.py +0 -172
  97. janito/plugins_backup_20250825_070018/core/imagedisplay/__init__.py +0 -14
  98. janito/plugins_backup_20250825_070018/core/imagedisplay/plugin.py +0 -51
  99. janito/plugins_backup_20250825_070018/core/imagedisplay/tools/__init__.py +0 -1
  100. janito/plugins_backup_20250825_070018/core/imagedisplay/tools/show_image.py +0 -83
  101. janito/plugins_backup_20250825_070018/core/imagedisplay/tools/show_image_grid.py +0 -84
  102. janito/plugins_backup_20250825_070018/core/system/__init__.py +0 -23
  103. janito/plugins_backup_20250825_070018/core/system/tools/run_bash_command.py +0 -183
  104. janito/plugins_backup_20250825_070018/core/system/tools/run_powershell_command.py +0 -218
  105. janito/plugins_backup_20250825_070018/dev/__init__.py +0 -7
  106. janito/plugins_backup_20250825_070018/dev/pythondev/__init__.py +0 -37
  107. janito/plugins_backup_20250825_070018/dev/pythondev/tools/python_code_run.py +0 -172
  108. janito/plugins_backup_20250825_070018/dev/pythondev/tools/python_command_run.py +0 -171
  109. janito/plugins_backup_20250825_070018/dev/pythondev/tools/python_file_run.py +0 -172
  110. janito/plugins_backup_20250825_070018/dev/visualization/__init__.py +0 -23
  111. janito/plugins_backup_20250825_070018/dev/visualization/tools/read_chart.py +0 -259
  112. janito/plugins_backup_20250825_070018/discovery.py +0 -289
  113. janito/plugins_backup_20250825_070018/example_plugin.py +0 -108
  114. janito/plugins_backup_20250825_070018/manager.py +0 -243
  115. janito/plugins_backup_20250825_070018/tools/__init__.py +0 -10
  116. janito/plugins_backup_20250825_070018/tools/copy_file.py +0 -87
  117. janito/plugins_backup_20250825_070018/tools/core_tools_plugin.py +0 -88
  118. janito/plugins_backup_20250825_070018/tools/create_directory.py +0 -70
  119. janito/plugins_backup_20250825_070018/tools/decorators.py +0 -19
  120. janito/plugins_backup_20250825_070018/tools/delete_text_in_file.py +0 -135
  121. janito/plugins_backup_20250825_070018/tools/find_files.py +0 -143
  122. janito/plugins_backup_20250825_070018/tools/get_file_outline/__init__.py +0 -7
  123. janito/plugins_backup_20250825_070018/tools/get_file_outline/core.py +0 -122
  124. janito/plugins_backup_20250825_070018/tools/get_file_outline/java_outline.py +0 -47
  125. janito/plugins_backup_20250825_070018/tools/get_file_outline/markdown_outline.py +0 -14
  126. janito/plugins_backup_20250825_070018/tools/get_file_outline/python_outline.py +0 -303
  127. janito/plugins_backup_20250825_070018/tools/get_file_outline/search_outline.py +0 -36
  128. janito/plugins_backup_20250825_070018/tools/move_file.py +0 -131
  129. janito/plugins_backup_20250825_070018/tools/open_html_in_browser.py +0 -51
  130. janito/plugins_backup_20250825_070018/tools/open_url.py +0 -37
  131. janito/plugins_backup_20250825_070018/tools/python_code_run.py +0 -172
  132. janito/plugins_backup_20250825_070018/tools/python_command_run.py +0 -171
  133. janito/plugins_backup_20250825_070018/tools/python_file_run.py +0 -172
  134. janito/plugins_backup_20250825_070018/tools/read_chart.py +0 -259
  135. janito/plugins_backup_20250825_070018/tools/read_files.py +0 -58
  136. janito/plugins_backup_20250825_070018/tools/remove_directory.py +0 -55
  137. janito/plugins_backup_20250825_070018/tools/remove_file.py +0 -58
  138. janito/plugins_backup_20250825_070018/tools/run_bash_command.py +0 -183
  139. janito/plugins_backup_20250825_070018/tools/run_powershell_command.py +0 -218
  140. janito/plugins_backup_20250825_070018/tools/search_text/__init__.py +0 -7
  141. janito/plugins_backup_20250825_070018/tools/search_text/core.py +0 -205
  142. janito/plugins_backup_20250825_070018/tools/search_text/match_lines.py +0 -67
  143. janito/plugins_backup_20250825_070018/tools/search_text/pattern_utils.py +0 -73
  144. janito/plugins_backup_20250825_070018/tools/search_text/traverse_directory.py +0 -145
  145. janito/plugins_backup_20250825_070018/tools/show_image_grid.py +0 -85
  146. janito/plugins_backup_20250825_070018/tools/validate_file_syntax/__init__.py +0 -7
  147. janito/plugins_backup_20250825_070018/tools/validate_file_syntax/core.py +0 -114
  148. janito/plugins_backup_20250825_070018/tools/validate_file_syntax/css_validator.py +0 -35
  149. janito/plugins_backup_20250825_070018/tools/validate_file_syntax/html_validator.py +0 -100
  150. janito/plugins_backup_20250825_070018/tools/validate_file_syntax/jinja2_validator.py +0 -50
  151. janito/plugins_backup_20250825_070018/tools/validate_file_syntax/js_validator.py +0 -27
  152. janito/plugins_backup_20250825_070018/tools/validate_file_syntax/json_validator.py +0 -6
  153. janito/plugins_backup_20250825_070018/tools/validate_file_syntax/markdown_validator.py +0 -109
  154. janito/plugins_backup_20250825_070018/tools/validate_file_syntax/ps1_validator.py +0 -32
  155. janito/plugins_backup_20250825_070018/tools/validate_file_syntax/python_validator.py +0 -5
  156. janito/plugins_backup_20250825_070018/tools/validate_file_syntax/xml_validator.py +0 -11
  157. janito/plugins_backup_20250825_070018/tools/validate_file_syntax/yaml_validator.py +0 -6
  158. janito/plugins_backup_20250825_070018/tools/view_file.py +0 -172
  159. janito/plugins_backup_20250825_070018/ui/__init__.py +0 -7
  160. janito/plugins_backup_20250825_070018/ui/userinterface/__init__.py +0 -16
  161. janito/plugins_backup_20250825_070018/ui/userinterface/tools/ask_user.py +0 -110
  162. janito/plugins_backup_20250825_070018/web/__init__.py +0 -7
  163. janito/plugins_backup_20250825_070018/web/webtools/__init__.py +0 -33
  164. janito/plugins_backup_20250825_070018/web/webtools/tools/fetch_url.py +0 -458
  165. janito/plugins_backup_20250825_070018/web/webtools/tools/open_html_in_browser.py +0 -51
  166. janito/plugins_backup_20250825_070018/web/webtools/tools/open_url.py +0 -37
  167. janito/tools/cli_initializer.py +0 -88
  168. janito/tools/initialize.py +0 -70
  169. janito-3.3.0.dist-info/RECORD +0 -400
  170. /janito/{plugins_backup_20250825_070018 → plugins}/auto_loader.py +0 -0
  171. /janito/{plugins_backup_20250825_070018 → plugins}/discovery_core.py +0 -0
  172. /janito/{plugins_backup_20250825_070018/core/filemanager/tools → tools/adapters/local}/copy_file.py +0 -0
  173. /janito/{plugins_backup_20250825_070018/core/filemanager/tools → tools/adapters/local}/create_directory.py +0 -0
  174. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/find_files.py +0 -0
  175. /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/get_file_outline/__init__.py +0 -0
  176. /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/get_file_outline/core.py +0 -0
  177. /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/get_file_outline/java_outline.py +0 -0
  178. /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/get_file_outline/markdown_outline.py +0 -0
  179. /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/get_file_outline/python_outline.py +0 -0
  180. /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/get_file_outline/search_outline.py +0 -0
  181. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/move_file.py +0 -0
  182. /janito/{plugins/web/webtools/tools → tools/adapters/local}/open_html_in_browser.py +0 -0
  183. /janito/{plugins/web/webtools/tools → tools/adapters/local}/open_url.py +0 -0
  184. /janito/{plugins/dev/pythondev/tools → tools/adapters/local}/python_code_run.py +0 -0
  185. /janito/{plugins/dev/pythondev/tools → tools/adapters/local}/python_command_run.py +0 -0
  186. /janito/{plugins/dev/pythondev/tools → tools/adapters/local}/python_file_run.py +0 -0
  187. /janito/{plugins/dev/visualization/tools → tools/adapters/local}/read_chart.py +0 -0
  188. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/read_files.py +0 -0
  189. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/remove_directory.py +0 -0
  190. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/remove_file.py +0 -0
  191. /janito/{plugins/core/system/tools → tools/adapters/local}/run_bash_command.py +0 -0
  192. /janito/{plugins/core/system/tools → tools/adapters/local}/run_powershell_command.py +0 -0
  193. /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/search_text/__init__.py +0 -0
  194. /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/search_text/core.py +0 -0
  195. /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/search_text/match_lines.py +0 -0
  196. /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/search_text/pattern_utils.py +0 -0
  197. /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/search_text/traverse_directory.py +0 -0
  198. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/__init__.py +0 -0
  199. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/core.py +0 -0
  200. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/css_validator.py +0 -0
  201. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/html_validator.py +0 -0
  202. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/jinja2_validator.py +0 -0
  203. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/js_validator.py +0 -0
  204. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/json_validator.py +0 -0
  205. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/markdown_validator.py +0 -0
  206. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/ps1_validator.py +0 -0
  207. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/python_validator.py +0 -0
  208. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/xml_validator.py +0 -0
  209. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/yaml_validator.py +0 -0
  210. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/view_file.py +0 -0
  211. {janito-3.3.0.dist-info → janito-3.4.0.dist-info}/WHEEL +0 -0
  212. {janito-3.3.0.dist-info → janito-3.4.0.dist-info}/entry_points.txt +0 -0
  213. {janito-3.3.0.dist-info → janito-3.4.0.dist-info}/licenses/LICENSE +0 -0
  214. {janito-3.3.0.dist-info → janito-3.4.0.dist-info}/top_level.txt +0 -0
@@ -1,458 +0,0 @@
1
- import requests
2
- import time
3
- import os
4
- import json
5
- from pathlib import Path
6
- from bs4 import BeautifulSoup
7
- from typing import Dict, Any, Optional
8
- from janito.tools.adapters.local.adapter import register_local_tool
9
- from janito.tools.tool_base import ToolBase, ToolPermissions
10
- from janito.report_events import ReportAction
11
- from janito.i18n import tr
12
- from janito.tools.tool_utils import pluralize
13
- from janito.tools.loop_protection_decorator import protect_against_loops
14
-
15
-
16
- @register_local_tool
17
- class FetchUrlTool(ToolBase):
18
- """
19
- Fetch the content of a web page and extract its text.
20
-
21
- This tool implements a **session-based caching mechanism** that provides
22
- **in-memory caching** for the lifetime of the tool instance. URLs are cached
23
- in RAM during the session, providing instant access to previously fetched
24
- content without making additional HTTP requests.
25
-
26
- **Session Cache Behavior:**
27
- - **Lifetime**: Cache exists for the lifetime of the FetchUrlTool instance
28
- - **Scope**: In-memory (RAM) cache, not persisted to disk
29
- - **Storage**: Successful responses are cached as raw HTML content
30
- - **Key**: Cache key is the exact URL string
31
- - **Invalidation**: Cache is automatically cleared when the tool instance is destroyed
32
- - **Performance**: Subsequent requests for the same URL return instantly
33
-
34
- **Error Cache Behavior:**
35
- - HTTP 403 errors: Cached for 24 hours (more permanent)
36
- - HTTP 404 errors: Cached for 1 hour (temporary)
37
- - Other 4xx errors: Cached for 30 minutes
38
- - 5xx errors: Not cached (retried on each request)
39
-
40
- Args:
41
- url (str): The URL of the web page to fetch.
42
- search_strings (list[str], optional): Strings to search for in the page content.
43
- max_length (int, optional): Maximum number of characters to return. Defaults to 5000.
44
- max_lines (int, optional): Maximum number of lines to return. Defaults to 200.
45
- context_chars (int, optional): Characters of context around search matches. Defaults to 400.
46
- timeout (int, optional): Timeout in seconds for the HTTP request. Defaults to 10.
47
- save_to_file (str, optional): File path to save the full resource content. If provided,
48
- the complete response will be saved to this file instead of being processed.
49
- headers (Dict[str, str], optional): Custom HTTP headers to send with the request.
50
- cookies (Dict[str, str], optional): Custom cookies to send with the request.
51
- follow_redirects (bool, optional): Whether to follow HTTP redirects. Defaults to True.
52
- Returns:
53
- str: Extracted text content from the web page, or a warning message. Example:
54
- - "<main text content...>"
55
- - "No lines found for the provided search strings."
56
- - "Warning: Empty URL provided. Operation skipped."
57
- """
58
-
59
- permissions = ToolPermissions(read=True)
60
- tool_name = "fetch_url"
61
-
62
- def __init__(self):
63
- super().__init__()
64
- self.cache_dir = Path.home() / ".janito" / "cache" / "fetch_url"
65
- self.cache_dir.mkdir(parents=True, exist_ok=True)
66
- self.cache_file = self.cache_dir / "error_cache.json"
67
- self.session_cache = (
68
- {}
69
- ) # In-memory session cache - lifetime matches tool instance
70
- self._load_cache()
71
-
72
- # Browser-like session with cookies and headers
73
- self.session = requests.Session()
74
- self.session.headers.update(
75
- {
76
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
77
- "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
78
- "Accept-Language": "en-US,en;q=0.5",
79
- "Accept-Encoding": "gzip, deflate, br",
80
- "DNT": "1",
81
- "Connection": "keep-alive",
82
- "Upgrade-Insecure-Requests": "1",
83
- }
84
- )
85
-
86
- # Load cookies from disk if they exist
87
- self.cookies_file = self.cache_dir / "cookies.json"
88
- self._load_cookies()
89
-
90
- def _load_cache(self):
91
- """Load error cache from disk."""
92
- if self.cache_file.exists():
93
- try:
94
- with open(self.cache_file, "r", encoding="utf-8") as f:
95
- self.error_cache = json.load(f)
96
- except (json.JSONDecodeError, IOError):
97
- self.error_cache = {}
98
- else:
99
- self.error_cache = {}
100
-
101
- def _save_cache(self):
102
- """Save error cache to disk."""
103
- try:
104
- with open(self.cache_file, "w", encoding="utf-8") as f:
105
- json.dump(self.error_cache, f, indent=2)
106
- except IOError:
107
- pass # Silently fail if we can't write cache
108
-
109
- def _load_cookies(self):
110
- """Load cookies from disk into session."""
111
- if self.cookies_file.exists():
112
- try:
113
- with open(self.cookies_file, "r", encoding="utf-8") as f:
114
- cookies_data = json.load(f)
115
- for cookie in cookies_data:
116
- self.session.cookies.set(**cookie)
117
- except (json.JSONDecodeError, IOError):
118
- pass # Silently fail if we can't load cookies
119
-
120
- def _save_cookies(self):
121
- """Save session cookies to disk."""
122
- try:
123
- cookies_data = []
124
- for cookie in self.session.cookies:
125
- cookies_data.append(
126
- {
127
- "name": cookie.name,
128
- "value": cookie.value,
129
- "domain": cookie.domain,
130
- "path": cookie.path,
131
- }
132
- )
133
- with open(self.cookies_file, "w", encoding="utf-8") as f:
134
- json.dump(cookies_data, f, indent=2)
135
- except IOError:
136
- pass # Silently fail if we can't write cookies
137
-
138
- def _get_cached_error(self, url: str) -> tuple[str, bool]:
139
- """
140
- Check if we have a cached error for this URL.
141
- Returns (error_message, is_cached) tuple.
142
- """
143
- if url not in self.error_cache:
144
- return None, False
145
-
146
- entry = self.error_cache[url]
147
- current_time = time.time()
148
-
149
- # Different expiration times for different status codes
150
- if entry["status_code"] == 403:
151
- # Cache 403 errors for 24 hours (more permanent)
152
- expiration_time = 24 * 3600
153
- elif entry["status_code"] == 404:
154
- # Cache 404 errors for 1 hour (more temporary)
155
- expiration_time = 3600
156
- else:
157
- # Cache other 4xx errors for 30 minutes
158
- expiration_time = 1800
159
-
160
- if current_time - entry["timestamp"] > expiration_time:
161
- # Cache expired, remove it
162
- del self.error_cache[url]
163
- self._save_cache()
164
- return None, False
165
-
166
- return entry["message"], True
167
-
168
- def _cache_error(self, url: str, status_code: int, message: str):
169
- """Cache an HTTP error response."""
170
- self.error_cache[url] = {
171
- "status_code": status_code,
172
- "message": message,
173
- "timestamp": time.time(),
174
- }
175
- self._save_cache()
176
-
177
- def _fetch_url_content(
178
- self,
179
- url: str,
180
- timeout: int = 10,
181
- headers: Optional[Dict[str, str]] = None,
182
- cookies: Optional[Dict[str, str]] = None,
183
- follow_redirects: bool = True,
184
- ) -> str:
185
- """Fetch URL content and handle HTTP errors.
186
-
187
- Implements two-tier caching:
188
- 1. Session cache: In-memory cache for successful responses (lifetime = tool instance)
189
- 2. Error cache: Persistent disk cache for HTTP errors with different expiration times
190
-
191
- Also implements URL whitelist checking and browser-like behavior.
192
- """
193
- # Check URL whitelist
194
- from janito.tools.url_whitelist import get_url_whitelist_manager
195
-
196
- whitelist_manager = get_url_whitelist_manager()
197
-
198
- if not whitelist_manager.is_url_allowed(url):
199
- error_message = tr("Blocked")
200
- self.report_error(
201
- tr("❗ Blocked"),
202
- ReportAction.READ,
203
- )
204
- return error_message
205
-
206
- # Check session cache first
207
- if url in self.session_cache:
208
- return self.session_cache[url]
209
-
210
- # Check persistent cache for known errors
211
- cached_error, is_cached = self._get_cached_error(url)
212
- if cached_error:
213
- self.report_warning(
214
- tr(
215
- "ℹ️ Using cached HTTP error for URL: {url}",
216
- url=url,
217
- ),
218
- ReportAction.READ,
219
- )
220
- return cached_error
221
-
222
- try:
223
- # Merge custom headers with default ones
224
- request_headers = self.session.headers.copy()
225
- if headers:
226
- request_headers.update(headers)
227
-
228
- # Merge custom cookies
229
- if cookies:
230
- self.session.cookies.update(cookies)
231
-
232
- response = self.session.get(
233
- url,
234
- timeout=timeout,
235
- headers=request_headers,
236
- allow_redirects=follow_redirects,
237
- )
238
- response.raise_for_status()
239
- content = response.text
240
-
241
- # Save cookies after successful request
242
- self._save_cookies()
243
-
244
- # Cache successful responses in session cache
245
- self.session_cache[url] = content
246
- return content
247
- except requests.exceptions.HTTPError as http_err:
248
- status_code = http_err.response.status_code if http_err.response else None
249
-
250
- # Map status codes to descriptions
251
- status_descriptions = {
252
- 400: "Bad Request",
253
- 401: "Unauthorized",
254
- 403: "Forbidden",
255
- 404: "Not Found",
256
- 405: "Method Not Allowed",
257
- 408: "Request Timeout",
258
- 409: "Conflict",
259
- 410: "Gone",
260
- 413: "Payload Too Large",
261
- 414: "URI Too Long",
262
- 415: "Unsupported Media Type",
263
- 429: "Too Many Requests",
264
- 500: "Internal Server Error",
265
- 501: "Not Implemented",
266
- 502: "Bad Gateway",
267
- 503: "Service Unavailable",
268
- 504: "Gateway Timeout",
269
- 505: "HTTP Version Not Supported",
270
- }
271
-
272
- if status_code and 400 <= status_code < 500:
273
- description = status_descriptions.get(status_code, "Client Error")
274
- error_message = f"HTTP {status_code} {description}"
275
- # Cache 403 and 404 errors
276
- if status_code in [403, 404]:
277
- self._cache_error(url, status_code, error_message)
278
-
279
- self.report_error(
280
- f"❗ HTTP {status_code} {description}",
281
- ReportAction.READ,
282
- )
283
- return error_message
284
- else:
285
- status_code_str = str(status_code) if status_code else "Error"
286
- description = status_descriptions.get(
287
- status_code,
288
- (
289
- "Server Error"
290
- if status_code and status_code >= 500
291
- else "Client Error"
292
- ),
293
- )
294
- self.report_error(
295
- f"❗ HTTP {status_code_str} {description}",
296
- ReportAction.READ,
297
- )
298
- return f"HTTP {status_code_str} {description}"
299
- except requests.exceptions.ConnectionError as conn_err:
300
- self.report_error(
301
- "❗ Network Error",
302
- ReportAction.READ,
303
- )
304
- return f"Network Error: Failed to connect to {url}"
305
- except requests.exceptions.Timeout as timeout_err:
306
- self.report_error(
307
- "❗ Timeout Error",
308
- ReportAction.READ,
309
- )
310
- return f"Timeout Error: Request timed out after {timeout} seconds"
311
- except requests.exceptions.RequestException as req_err:
312
- self.report_error(
313
- "❗ Request Error",
314
- ReportAction.READ,
315
- )
316
- return f"Request Error: {str(req_err)}"
317
- except Exception as err:
318
- self.report_error(
319
- "❗ Error fetching URL",
320
- ReportAction.READ,
321
- )
322
- return f"Error: {str(err)}"
323
-
324
- def _extract_and_clean_text(self, html_content: str) -> str:
325
- """Extract and clean text from HTML content."""
326
- soup = BeautifulSoup(html_content, "html.parser")
327
- text = soup.get_text(separator="\n")
328
-
329
- # Clean up excessive whitespace
330
- lines = [line.strip() for line in text.splitlines() if line.strip()]
331
- return "\n".join(lines)
332
-
333
- def _filter_by_search_strings(
334
- self, text: str, search_strings: list[str], context_chars: int
335
- ) -> str:
336
- """Filter text by search strings with context."""
337
- filtered = []
338
- for s in search_strings:
339
- idx = text.find(s)
340
- if idx != -1:
341
- start = max(0, idx - context_chars)
342
- end = min(len(text), idx + len(s) + context_chars)
343
- snippet = text[start:end]
344
- filtered.append(snippet)
345
-
346
- if filtered:
347
- return "\n...\n".join(filtered)
348
- else:
349
- return tr("No lines found for the provided search strings.")
350
-
351
- def _apply_limits(self, text: str, max_length: int, max_lines: int) -> str:
352
- """Apply length and line limits to text."""
353
- # Apply length limit
354
- if len(text) > max_length:
355
- text = text[:max_length] + "\n... (content truncated due to length limit)"
356
-
357
- # Apply line limit
358
- lines = text.splitlines()
359
- if len(lines) > max_lines:
360
- text = (
361
- "\n".join(lines[:max_lines])
362
- + "\n... (content truncated due to line limit)"
363
- )
364
-
365
- return text
366
-
367
- @protect_against_loops(max_calls=5, time_window=10.0, key_field="url")
368
- def run(
369
- self,
370
- url: str,
371
- search_strings: list[str] = None,
372
- max_length: int = 5000,
373
- max_lines: int = 200,
374
- context_chars: int = 400,
375
- timeout: int = 10,
376
- save_to_file: str = None,
377
- headers: Dict[str, str] = None,
378
- cookies: Dict[str, str] = None,
379
- follow_redirects: bool = True,
380
- ) -> str:
381
- if not url.strip():
382
- self.report_warning(tr("ℹ️ Empty URL provided."), ReportAction.READ)
383
- return tr("Warning: Empty URL provided. Operation skipped.")
384
-
385
- self.report_action(tr("🌐 Fetch URL '{url}' ...", url=url), ReportAction.READ)
386
-
387
- # Check if we should save to file
388
- if save_to_file:
389
- html_content = self._fetch_url_content(
390
- url,
391
- timeout=timeout,
392
- headers=headers,
393
- cookies=cookies,
394
- follow_redirects=follow_redirects,
395
- )
396
- if (
397
- html_content.startswith("HTTP Error ")
398
- or html_content == "Error"
399
- or html_content == "Blocked"
400
- ):
401
- return html_content
402
-
403
- try:
404
- with open(save_to_file, "w", encoding="utf-8") as f:
405
- f.write(html_content)
406
- file_size = len(html_content)
407
- self.report_success(
408
- tr(
409
- "✅ Saved {size} bytes to {file}",
410
- size=file_size,
411
- file=save_to_file,
412
- ),
413
- ReportAction.READ,
414
- )
415
- return tr("Successfully saved content to: {file}", file=save_to_file)
416
- except IOError as e:
417
- error_msg = tr("Error saving to file: {error}", error=str(e))
418
- self.report_error(error_msg, ReportAction.READ)
419
- return error_msg
420
-
421
- # Normal processing path
422
- html_content = self._fetch_url_content(
423
- url,
424
- timeout=timeout,
425
- headers=headers,
426
- cookies=cookies,
427
- follow_redirects=follow_redirects,
428
- )
429
- if (
430
- html_content.startswith("HTTP Error ")
431
- or html_content == "Error"
432
- or html_content == "Blocked"
433
- ):
434
- return html_content
435
-
436
- # Extract and clean text
437
- text = self._extract_and_clean_text(html_content)
438
-
439
- # Filter by search strings if provided
440
- if search_strings:
441
- text = self._filter_by_search_strings(text, search_strings, context_chars)
442
-
443
- # Apply limits
444
- text = self._apply_limits(text, max_length, max_lines)
445
-
446
- # Report success
447
- num_lines = len(text.splitlines())
448
- total_chars = len(text)
449
- self.report_success(
450
- tr(
451
- "✅ {num_lines} {line_word}, {chars} chars",
452
- num_lines=num_lines,
453
- line_word=pluralize("line", num_lines),
454
- chars=total_chars,
455
- ),
456
- ReportAction.READ,
457
- )
458
- return text
@@ -1,36 +0,0 @@
1
- """
2
- Plugin System for Development Tools
3
-
4
- This package organizes all available tools into logical plugin groups
5
- for easier discovery and usage.
6
- """
7
-
8
- __version__ = "1.0.0"
9
- __author__ = "Development Assistant"
10
-
11
- from .core import filemanager, codeanalyzer, system, imagedisplay
12
- from .web import webtools
13
- from .dev import pythondev, visualization
14
- from .ui import userinterface
15
-
16
- # Plugin registry
17
- PLUGINS = {
18
- "core.filemanager": filemanager,
19
- "core.codeanalyzer": codeanalyzer,
20
- "core.system": system,
21
- "core.imagedisplay": imagedisplay,
22
- "web.webtools": webtools,
23
- "dev.pythondev": pythondev,
24
- "dev.visualization": visualization,
25
- "ui.userinterface": userinterface,
26
- }
27
-
28
-
29
- def list_plugins():
30
- """Return all available plugins"""
31
- return list(PLUGINS.keys())
32
-
33
-
34
- def get_plugin(name):
35
- """Get a specific plugin by name"""
36
- return PLUGINS.get(name)
@@ -1,102 +0,0 @@
1
- """
2
- Builtin plugin system for janito-packaged plugins.
3
-
4
- This module provides the infrastructure for plugins that are bundled
5
- with janito and available by default without requiring external installation.
6
- """
7
-
8
- import importlib
9
- from typing import Dict, List, Optional, Type
10
- from janito.plugin_system.base import Plugin
11
-
12
-
13
- class BuiltinPluginRegistry:
14
- """Registry for builtin plugins that come packaged with janito."""
15
-
16
- _plugins: Dict[str, Type[Plugin]] = {}
17
-
18
- @classmethod
19
- def register(cls, name: str, plugin_class: Type[Plugin]) -> None:
20
- """Register a builtin plugin."""
21
- cls._plugins[name] = plugin_class
22
-
23
- @classmethod
24
- def get_plugin_class(cls, name: str) -> Optional[Type[Plugin]]:
25
- """Get the plugin class for a builtin plugin."""
26
- return cls._plugins.get(name)
27
-
28
- @classmethod
29
- def list_builtin_plugins(cls) -> List[str]:
30
- """List all registered builtin plugins."""
31
- return list(cls._plugins.keys())
32
-
33
- @classmethod
34
- def is_builtin(cls, name: str) -> bool:
35
- """Check if a plugin is builtin."""
36
- return name in cls._plugins
37
-
38
-
39
- def register_builtin_plugin(name: str):
40
- """Decorator to register a plugin as builtin."""
41
-
42
- def decorator(plugin_class: Type[Plugin]) -> Type[Plugin]:
43
- BuiltinPluginRegistry.register(name, plugin_class)
44
- return plugin_class
45
-
46
- return decorator
47
-
48
-
49
- def load_builtin_plugin(name: str) -> Optional[Plugin]:
50
- """Load a builtin plugin by name."""
51
- plugin_class = BuiltinPluginRegistry.get_plugin_class(name)
52
- if plugin_class:
53
- return plugin_class()
54
- return None
55
-
56
-
57
- # Auto-register janito-coder plugins as builtin
58
- try:
59
- from janito_coder.plugins import (
60
- GitAnalyzerPlugin,
61
- CodeNavigatorPlugin,
62
- DependencyAnalyzerPlugin,
63
- CodeFormatterPlugin,
64
- TestRunnerPlugin,
65
- LinterPlugin,
66
- DebuggerPlugin,
67
- PerformanceProfilerPlugin,
68
- SecurityScannerPlugin,
69
- DocumentationGeneratorPlugin,
70
- )
71
-
72
- # Register all janito-coder plugins as builtin
73
- BuiltinPluginRegistry.register("git_analyzer", GitAnalyzerPlugin)
74
- BuiltinPluginRegistry.register("code_navigator", CodeNavigatorPlugin)
75
- BuiltinPluginRegistry.register("dependency_analyzer", DependencyAnalyzerPlugin)
76
- BuiltinPluginRegistry.register("code_formatter", CodeFormatterPlugin)
77
- BuiltinPluginRegistry.register("test_runner", TestRunnerPlugin)
78
- BuiltinPluginRegistry.register("linter", LinterPlugin)
79
- BuiltinPluginRegistry.register("debugger", DebuggerPlugin)
80
- BuiltinPluginRegistry.register("performance_profiler", PerformanceProfilerPlugin)
81
- BuiltinPluginRegistry.register("security_scanner", SecurityScannerPlugin)
82
- BuiltinPluginRegistry.register(
83
- "documentation_generator", DocumentationGeneratorPlugin
84
- )
85
-
86
- # Register core tools plugin
87
- from janito.plugins.tools import CoreToolsPlugin
88
-
89
- BuiltinPluginRegistry.register("core_tools", CoreToolsPlugin)
90
-
91
- except ImportError:
92
- # janito-coder not available, skip registration
93
- pass
94
-
95
- # Register core tools plugin (always available)
96
- try:
97
- from janito.plugins.tools import CoreToolsPlugin
98
-
99
- BuiltinPluginRegistry.register("core_tools", CoreToolsPlugin)
100
- except ImportError:
101
- # Should not happen, but handle gracefully
102
- pass
@@ -1,84 +0,0 @@
1
- """
2
- Configuration management for plugins using user directory.
3
- """
4
-
5
- import json
6
- import os
7
- from pathlib import Path
8
- from typing import Dict, Any, Optional
9
-
10
-
11
- def get_user_config_dir() -> Path:
12
- """Get the user configuration directory."""
13
- return Path.home() / ".janito"
14
-
15
-
16
- def get_plugins_config_path() -> Path:
17
- """Get the path to the plugins configuration file."""
18
- return get_user_config_dir() / "plugins.json"
19
-
20
-
21
- def load_plugins_config() -> Dict[str, Any]:
22
- """
23
- Load plugins configuration from user directory.
24
-
25
- Returns:
26
- Dict containing plugins configuration
27
- """
28
- config_path = get_plugins_config_path()
29
-
30
- if not config_path.exists():
31
- # Create default config if it doesn't exist
32
- default_config = {
33
- "plugins": {
34
- "paths": [str(Path.home() / ".janito" / "plugins"), "./plugins"],
35
- "load": {},
36
- }
37
- }
38
-
39
- # Ensure directory exists
40
- config_path.parent.mkdir(parents=True, exist_ok=True)
41
-
42
- # Save default config
43
- with open(config_path, "w") as f:
44
- json.dump(default_config, f, indent=2)
45
-
46
- return default_config
47
-
48
- try:
49
- with open(config_path, "r") as f:
50
- return json.load(f)
51
- except (json.JSONDecodeError, IOError) as e:
52
- print(f"Warning: Failed to load plugins config from {config_path}: {e}")
53
- return {"plugins": {"paths": [], "load": {}}}
54
-
55
-
56
- def save_plugins_config(config: Dict[str, Any]) -> bool:
57
- """
58
- Save plugins configuration to user directory.
59
-
60
- Args:
61
- config: Configuration dict to save
62
-
63
- Returns:
64
- True if saved successfully
65
- """
66
- config_path = get_plugins_config_path()
67
-
68
- try:
69
- # Ensure directory exists
70
- config_path.parent.mkdir(parents=True, exist_ok=True)
71
-
72
- with open(config_path, "w") as f:
73
- json.dump(config, f, indent=2)
74
- return True
75
- except IOError as e:
76
- print(f"Error: Failed to save plugins config to {config_path}: {e}")
77
- return False
78
-
79
-
80
- def get_user_plugins_dir() -> Path:
81
- """Get the user plugins directory."""
82
- plugins_dir = get_user_config_dir() / "plugins"
83
- plugins_dir.mkdir(parents=True, exist_ok=True)
84
- return plugins_dir
@@ -1,7 +0,0 @@
1
- """
2
- Core Plugin Package
3
-
4
- Contains essential system and file management plugins.
5
- """
6
-
7
- __all__ = ["filemanager", "codeanalyzer", "system"]