janito 1.14.2__py3-none-any.whl → 2.0.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 (282) hide show
  1. janito/__init__.py +6 -1
  2. janito/__main__.py +1 -1
  3. janito/agent/setup_agent.py +139 -0
  4. janito/agent/templates/profiles/{system_prompt_template_base.txt.j2 → system_prompt_template_main.txt.j2} +1 -1
  5. janito/cli/__init__.py +9 -0
  6. janito/cli/chat_mode/bindings.py +37 -0
  7. janito/cli/chat_mode/chat_entry.py +23 -0
  8. janito/cli/chat_mode/prompt_style.py +19 -0
  9. janito/cli/chat_mode/session.py +272 -0
  10. janito/{shell/prompt/completer.py → cli/chat_mode/shell/autocomplete.py} +7 -6
  11. janito/cli/chat_mode/shell/commands/__init__.py +55 -0
  12. janito/cli/chat_mode/shell/commands/base.py +9 -0
  13. janito/cli/chat_mode/shell/commands/clear.py +12 -0
  14. janito/{shell → cli/chat_mode/shell}/commands/conversation_restart.py +34 -30
  15. janito/cli/chat_mode/shell/commands/edit.py +25 -0
  16. janito/cli/chat_mode/shell/commands/help.py +16 -0
  17. janito/cli/chat_mode/shell/commands/history_view.py +93 -0
  18. janito/cli/chat_mode/shell/commands/lang.py +25 -0
  19. janito/cli/chat_mode/shell/commands/last.py +137 -0
  20. janito/cli/chat_mode/shell/commands/livelogs.py +49 -0
  21. janito/cli/chat_mode/shell/commands/multi.py +51 -0
  22. janito/cli/chat_mode/shell/commands/prompt.py +64 -0
  23. janito/cli/chat_mode/shell/commands/role.py +36 -0
  24. janito/cli/chat_mode/shell/commands/session.py +40 -0
  25. janito/{shell → cli/chat_mode/shell}/commands/session_control.py +2 -2
  26. janito/cli/chat_mode/shell/commands/termweb_log.py +92 -0
  27. janito/cli/chat_mode/shell/commands/tools.py +32 -0
  28. janito/{shell → cli/chat_mode/shell}/commands/utility.py +4 -7
  29. janito/{shell → cli/chat_mode/shell}/commands/verbose.py +5 -5
  30. janito/cli/chat_mode/shell/session/__init__.py +1 -0
  31. janito/{shell → cli/chat_mode/shell}/session/manager.py +9 -1
  32. janito/cli/chat_mode/toolbar.py +90 -0
  33. janito/cli/cli_commands/list_models.py +35 -0
  34. janito/cli/cli_commands/list_providers.py +9 -0
  35. janito/cli/cli_commands/list_tools.py +53 -0
  36. janito/cli/cli_commands/model_selection.py +50 -0
  37. janito/cli/cli_commands/model_utils.py +84 -0
  38. janito/cli/cli_commands/set_api_key.py +19 -0
  39. janito/cli/cli_commands/show_config.py +51 -0
  40. janito/cli/cli_commands/show_system_prompt.py +62 -0
  41. janito/cli/config.py +28 -0
  42. janito/cli/console.py +3 -0
  43. janito/cli/core/__init__.py +4 -0
  44. janito/cli/core/event_logger.py +59 -0
  45. janito/cli/core/getters.py +31 -0
  46. janito/cli/core/runner.py +141 -0
  47. janito/cli/core/setters.py +174 -0
  48. janito/cli/core/unsetters.py +54 -0
  49. janito/cli/main.py +8 -196
  50. janito/cli/main_cli.py +312 -0
  51. janito/cli/prompt_core.py +230 -0
  52. janito/cli/prompt_handler.py +6 -0
  53. janito/cli/rich_terminal_reporter.py +101 -0
  54. janito/cli/single_shot_mode/__init__.py +6 -0
  55. janito/cli/single_shot_mode/handler.py +137 -0
  56. janito/cli/termweb_starter.py +73 -24
  57. janito/cli/utils.py +25 -0
  58. janito/cli/verbose_output.py +196 -0
  59. janito/config.py +5 -0
  60. janito/config_manager.py +110 -0
  61. janito/conversation_history.py +30 -0
  62. janito/{agent/tools_utils/dir_walk_utils.py → dir_walk_utils.py} +3 -2
  63. janito/driver_events.py +98 -0
  64. janito/drivers/anthropic/driver.py +113 -0
  65. janito/drivers/azure_openai/driver.py +36 -0
  66. janito/drivers/driver_registry.py +33 -0
  67. janito/drivers/google_genai/driver.py +54 -0
  68. janito/drivers/google_genai/schema_generator.py +67 -0
  69. janito/drivers/mistralai/driver.py +41 -0
  70. janito/drivers/openai/driver.py +334 -0
  71. janito/event_bus/__init__.py +2 -0
  72. janito/event_bus/bus.py +68 -0
  73. janito/event_bus/event.py +15 -0
  74. janito/event_bus/handler.py +31 -0
  75. janito/event_bus/queue_bus.py +57 -0
  76. janito/exceptions.py +23 -0
  77. janito/formatting_token.py +54 -0
  78. janito/i18n/pt.py +1 -0
  79. janito/llm/__init__.py +5 -0
  80. janito/llm/agent.py +443 -0
  81. janito/llm/auth.py +62 -0
  82. janito/llm/driver.py +239 -0
  83. janito/llm/driver_config.py +34 -0
  84. janito/llm/driver_config_builder.py +34 -0
  85. janito/llm/driver_input.py +12 -0
  86. janito/llm/message_parts.py +60 -0
  87. janito/llm/model.py +38 -0
  88. janito/llm/provider.py +187 -0
  89. janito/perf_singleton.py +3 -0
  90. janito/performance_collector.py +167 -0
  91. janito/provider_config.py +98 -0
  92. janito/provider_registry.py +152 -0
  93. janito/providers/__init__.py +7 -0
  94. janito/providers/anthropic/model_info.py +22 -0
  95. janito/providers/anthropic/provider.py +65 -0
  96. janito/providers/azure_openai/model_info.py +15 -0
  97. janito/providers/azure_openai/provider.py +72 -0
  98. janito/providers/deepseek/__init__.py +1 -0
  99. janito/providers/deepseek/model_info.py +16 -0
  100. janito/providers/deepseek/provider.py +91 -0
  101. janito/providers/google/__init__.py +1 -0
  102. janito/providers/google/model_info.py +40 -0
  103. janito/providers/google/provider.py +69 -0
  104. janito/providers/mistralai/model_info.py +37 -0
  105. janito/providers/mistralai/provider.py +69 -0
  106. janito/providers/openai/__init__.py +1 -0
  107. janito/providers/openai/model_info.py +137 -0
  108. janito/providers/openai/provider.py +107 -0
  109. janito/providers/openai/schema_generator.py +63 -0
  110. janito/providers/provider_static_info.py +21 -0
  111. janito/providers/registry.py +26 -0
  112. janito/report_events.py +38 -0
  113. janito/termweb/app.py +1 -1
  114. janito/tools/__init__.py +16 -0
  115. janito/tools/adapters/__init__.py +1 -0
  116. janito/tools/adapters/local/__init__.py +54 -0
  117. janito/tools/adapters/local/adapter.py +92 -0
  118. janito/{agent/tools → tools/adapters/local}/ask_user.py +30 -13
  119. janito/tools/adapters/local/copy_file.py +84 -0
  120. janito/{agent/tools → tools/adapters/local}/create_directory.py +11 -10
  121. janito/tools/adapters/local/create_file.py +82 -0
  122. janito/tools/adapters/local/delete_text_in_file.py +136 -0
  123. janito/{agent/tools → tools/adapters/local}/fetch_url.py +18 -19
  124. janito/tools/adapters/local/find_files.py +140 -0
  125. janito/tools/adapters/local/get_file_outline/core.py +151 -0
  126. janito/{agent/tools → tools/adapters/local}/get_file_outline/python_outline.py +125 -0
  127. janito/tools/adapters/local/get_file_outline/python_outline_v2.py +156 -0
  128. janito/{agent/tools → tools/adapters/local}/get_file_outline/search_outline.py +12 -7
  129. janito/{agent/tools → tools/adapters/local}/move_file.py +13 -9
  130. janito/{agent/tools → tools/adapters/local}/open_url.py +7 -5
  131. janito/tools/adapters/local/python_code_run.py +165 -0
  132. janito/tools/adapters/local/python_command_run.py +163 -0
  133. janito/tools/adapters/local/python_file_run.py +162 -0
  134. janito/{agent/tools → tools/adapters/local}/remove_directory.py +15 -9
  135. janito/{agent/tools → tools/adapters/local}/remove_file.py +17 -14
  136. janito/{agent/tools → tools/adapters/local}/replace_text_in_file.py +27 -22
  137. janito/tools/adapters/local/run_bash_command.py +176 -0
  138. janito/tools/adapters/local/run_powershell_command.py +219 -0
  139. janito/{agent/tools → tools/adapters/local}/search_text/core.py +32 -12
  140. janito/{agent/tools → tools/adapters/local}/search_text/match_lines.py +13 -4
  141. janito/{agent/tools → tools/adapters/local}/search_text/pattern_utils.py +12 -4
  142. janito/{agent/tools → tools/adapters/local}/search_text/traverse_directory.py +15 -2
  143. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/core.py +12 -11
  144. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/css_validator.py +1 -1
  145. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/html_validator.py +1 -1
  146. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/js_validator.py +1 -1
  147. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/json_validator.py +1 -1
  148. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/markdown_validator.py +1 -1
  149. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/ps1_validator.py +1 -1
  150. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/python_validator.py +1 -1
  151. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/xml_validator.py +1 -1
  152. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/yaml_validator.py +1 -1
  153. janito/{agent/tools/get_lines.py → tools/adapters/local/view_file.py} +45 -27
  154. janito/tools/inspect_registry.py +17 -0
  155. janito/tools/tool_base.py +105 -0
  156. janito/tools/tool_events.py +58 -0
  157. janito/tools/tool_run_exception.py +12 -0
  158. janito/{agent → tools}/tool_use_tracker.py +2 -4
  159. janito/{agent/tools_utils/utils.py → tools/tool_utils.py} +18 -9
  160. janito/tools/tools_adapter.py +207 -0
  161. janito/tools/tools_schema.py +104 -0
  162. janito/utils.py +11 -0
  163. janito/version.py +4 -0
  164. janito-2.0.0.dist-info/METADATA +232 -0
  165. janito-2.0.0.dist-info/RECORD +180 -0
  166. janito/agent/__init__.py +0 -0
  167. janito/agent/api_exceptions.py +0 -4
  168. janito/agent/config.py +0 -147
  169. janito/agent/config_defaults.py +0 -12
  170. janito/agent/config_utils.py +0 -0
  171. janito/agent/content_handler.py +0 -0
  172. janito/agent/conversation.py +0 -238
  173. janito/agent/conversation_api.py +0 -306
  174. janito/agent/conversation_exceptions.py +0 -18
  175. janito/agent/conversation_tool_calls.py +0 -39
  176. janito/agent/conversation_ui.py +0 -17
  177. janito/agent/event.py +0 -24
  178. janito/agent/event_dispatcher.py +0 -24
  179. janito/agent/event_handler_protocol.py +0 -5
  180. janito/agent/event_system.py +0 -15
  181. janito/agent/llm_conversation_history.py +0 -82
  182. janito/agent/message_handler.py +0 -20
  183. janito/agent/message_handler_protocol.py +0 -5
  184. janito/agent/openai_client.py +0 -149
  185. janito/agent/openai_schema_generator.py +0 -187
  186. janito/agent/profile_manager.py +0 -96
  187. janito/agent/queued_message_handler.py +0 -50
  188. janito/agent/rich_live.py +0 -32
  189. janito/agent/rich_message_handler.py +0 -115
  190. janito/agent/runtime_config.py +0 -36
  191. janito/agent/test_handler_protocols.py +0 -47
  192. janito/agent/test_openai_schema_generator.py +0 -93
  193. janito/agent/tests/__init__.py +0 -1
  194. janito/agent/tool_base.py +0 -63
  195. janito/agent/tool_executor.py +0 -122
  196. janito/agent/tool_registry.py +0 -49
  197. janito/agent/tools/__init__.py +0 -47
  198. janito/agent/tools/create_file.py +0 -59
  199. janito/agent/tools/delete_text_in_file.py +0 -97
  200. janito/agent/tools/find_files.py +0 -106
  201. janito/agent/tools/get_file_outline/core.py +0 -81
  202. janito/agent/tools/present_choices.py +0 -64
  203. janito/agent/tools/python_command_runner.py +0 -201
  204. janito/agent/tools/python_file_runner.py +0 -199
  205. janito/agent/tools/python_stdin_runner.py +0 -208
  206. janito/agent/tools/replace_file.py +0 -72
  207. janito/agent/tools/run_bash_command.py +0 -218
  208. janito/agent/tools/run_powershell_command.py +0 -251
  209. janito/agent/tools_utils/__init__.py +0 -1
  210. janito/agent/tools_utils/action_type.py +0 -7
  211. janito/agent/tools_utils/test_gitignore_utils.py +0 -46
  212. janito/cli/_livereload_log_utils.py +0 -13
  213. janito/cli/_print_config.py +0 -96
  214. janito/cli/_termweb_log_utils.py +0 -17
  215. janito/cli/_utils.py +0 -9
  216. janito/cli/arg_parser.py +0 -272
  217. janito/cli/cli_main.py +0 -281
  218. janito/cli/config_commands.py +0 -211
  219. janito/cli/config_runner.py +0 -35
  220. janito/cli/formatting_runner.py +0 -12
  221. janito/cli/livereload_starter.py +0 -60
  222. janito/cli/logging_setup.py +0 -38
  223. janito/cli/one_shot.py +0 -80
  224. janito/livereload/app.py +0 -25
  225. janito/rich_utils.py +0 -59
  226. janito/shell/__init__.py +0 -0
  227. janito/shell/commands/__init__.py +0 -61
  228. janito/shell/commands/config.py +0 -22
  229. janito/shell/commands/edit.py +0 -24
  230. janito/shell/commands/history_view.py +0 -18
  231. janito/shell/commands/lang.py +0 -19
  232. janito/shell/commands/livelogs.py +0 -42
  233. janito/shell/commands/prompt.py +0 -62
  234. janito/shell/commands/termweb_log.py +0 -94
  235. janito/shell/commands/tools.py +0 -26
  236. janito/shell/commands/track.py +0 -36
  237. janito/shell/main.py +0 -326
  238. janito/shell/prompt/load_prompt.py +0 -57
  239. janito/shell/prompt/session_setup.py +0 -57
  240. janito/shell/session/config.py +0 -109
  241. janito/shell/session/history.py +0 -0
  242. janito/shell/ui/interactive.py +0 -226
  243. janito/termweb/static/editor.css +0 -158
  244. janito/termweb/static/editor.css.bak +0 -145
  245. janito/termweb/static/editor.html +0 -46
  246. janito/termweb/static/editor.html.bak +0 -46
  247. janito/termweb/static/editor.js +0 -265
  248. janito/termweb/static/editor.js.bak +0 -259
  249. janito/termweb/static/explorer.html.bak +0 -59
  250. janito/termweb/static/favicon.ico +0 -0
  251. janito/termweb/static/favicon.ico.bak +0 -0
  252. janito/termweb/static/index.html +0 -53
  253. janito/termweb/static/index.html.bak +0 -54
  254. janito/termweb/static/index.html.bak.bak +0 -175
  255. janito/termweb/static/landing.html.bak +0 -36
  256. janito/termweb/static/termicon.svg +0 -1
  257. janito/termweb/static/termweb.css +0 -214
  258. janito/termweb/static/termweb.css.bak +0 -237
  259. janito/termweb/static/termweb.js +0 -162
  260. janito/termweb/static/termweb.js.bak +0 -168
  261. janito/termweb/static/termweb.js.bak.bak +0 -157
  262. janito/termweb/static/termweb_quickopen.js +0 -135
  263. janito/termweb/static/termweb_quickopen.js.bak +0 -125
  264. janito/tests/test_rich_utils.py +0 -44
  265. janito/web/__init__.py +0 -0
  266. janito/web/__main__.py +0 -25
  267. janito/web/app.py +0 -145
  268. janito-1.14.2.dist-info/METADATA +0 -306
  269. janito-1.14.2.dist-info/RECORD +0 -162
  270. janito-1.14.2.dist-info/licenses/LICENSE +0 -21
  271. /janito/{shell → cli/chat_mode/shell}/input_history.py +0 -0
  272. /janito/{shell/commands/session.py → cli/chat_mode/shell/session/history.py} +0 -0
  273. /janito/{agent/tools_utils/formatting.py → formatting.py} +0 -0
  274. /janito/{agent/tools_utils/gitignore_utils.py → gitignore_utils.py} +0 -0
  275. /janito/{agent/platform_discovery.py → platform_discovery.py} +0 -0
  276. /janito/{agent/tools → tools/adapters/local}/get_file_outline/__init__.py +0 -0
  277. /janito/{agent/tools → tools/adapters/local}/get_file_outline/markdown_outline.py +0 -0
  278. /janito/{agent/tools → tools/adapters/local}/search_text/__init__.py +0 -0
  279. /janito/{agent/tools → tools/adapters/local}/validate_file_syntax/__init__.py +0 -0
  280. {janito-1.14.2.dist-info → janito-2.0.0.dist-info}/WHEEL +0 -0
  281. {janito-1.14.2.dist-info → janito-2.0.0.dist-info}/entry_points.txt +0 -0
  282. {janito-1.14.2.dist-info → janito-2.0.0.dist-info}/top_level.txt +0 -0
@@ -1,157 +0,0 @@
1
- // Directory/File browser logic for explorer with right-side preview and URL sync (using /path)
2
- let explorerView = localStorage.getItem('explorerView') || 'list';
3
- let currentExplorerPath = '.';
4
-
5
- function getParentPath(path) {
6
- // Normalize slashes
7
- path = (path || '').replace(/\\/g, '/').replace(/^\/+|\/+$/g, '');
8
- if (!path || path === '.' || path === '') return '.';
9
- const parts = path.split('/');
10
- if (parts.length <= 1) return '.';
11
- parts.pop();
12
- const parent = parts.join('/');
13
- return parent === '' ? '.' : parent;
14
- }
15
-
16
-
17
- function setExplorerView(view) {
18
- explorerView = view;
19
- localStorage.setItem('explorerView', view);
20
- document.getElementById('view-list').classList.toggle('active', view === 'list');
21
- document.getElementById('view-icons').classList.toggle('active', view === 'icons');
22
- }
23
-
24
- function normalizeExplorerPath(path) {
25
- if (!path || path === '/' || path === '' || path === '.') return '.';
26
- return path.replace(/^\/+|\/+$/g, '');
27
- }
28
-
29
- function updateExplorerUrl(path, push=true) {
30
- let url = '/';
31
- if (path && path !== '.' && path !== '/') {
32
- url = '/' + path.replace(/^\/+|\/+$/g, '');
33
- }
34
- if (push) {
35
- console.log('[DEBUG] updateExplorerUrl: pushing url', url, 'for path', path);
36
- window.history.pushState({ explorerPath: path }, '', url);
37
- } else {
38
- console.log('[DEBUG] updateExplorerUrl: not pushing, url would be', url, 'for path', path);
39
- }
40
- }
41
-
42
- function renderExplorer(path, pushUrl=true) {
43
- currentExplorerPath = normalizeExplorerPath(path);
44
- console.log('[DEBUG] renderExplorer: path', path, 'normalized to', currentExplorerPath, 'pushUrl:', pushUrl);
45
- fetch(`/api/explorer/${encodeURIComponent(currentExplorerPath)}`)
46
- .then(resp => resp.json())
47
- .then(data => {
48
- const main = document.getElementById('explorer-main');
49
- if (!main) return;
50
- if (data.error) {
51
- main.innerHTML = `<div class='error'>${data.error}</div>`;
52
- return;
53
- }
54
- if (data.type === 'dir') {
55
- let html = `<h3>Directory: ${data.path}</h3>`;
56
- if (explorerView === 'list') {
57
- html += `<ul class='explorer-list'>`;
58
- if (data.path !== '.') {
59
- const parent = getParentPath(data.path);
60
- html += `<li><a href='#' data-path='${parent}' class='explorer-link'>(.. parent)</a></li>`;
61
- }
62
- for (const entry of data.entries) {
63
- const entryPath = data.path === '.' ? entry.name : data.path + '/' + entry.name;
64
- if (entry.is_dir) {
65
- html += `<li><a href='#' data-path='${entryPath}' class='explorer-link'>📁 ${entry.name}</a></li>`;
66
- } else {
67
- html += `<li><a href='#' data-path='${entryPath}' class='explorer-link file-link'>📄 ${entry.name}</a></li>`;
68
- }
69
- }
70
- html += '</ul>';
71
- } else {
72
- html += `<div class='explorer-icons'>`;
73
- if (data.path !== '.') {
74
- const parent = getParentPath(data.path);
75
- html += `<div class='explorer-icon'><a href='#' data-path='${parent}' class='explorer-link' title='Parent'>(..)</a></div>`;
76
- }
77
- for (const entry of data.entries) {
78
- const entryPath = data.path === '.' ? entry.name : data.path + '/' + entry.name;
79
- if (entry.is_dir) {
80
- html += `<div class='explorer-icon'><a href='#' data-path='${entryPath}' class='explorer-link' title='${entry.name}'>📁<br>${entry.name}</a></div>`;
81
- } else {
82
- html += `<div class='explorer-icon'><a href='#' data-path='${entryPath}' class='explorer-link file-link' title='${entry.name}'>📄<br>${entry.name}</a></div>`;
83
- }
84
- }
85
- html += '</div>';
86
- }
87
- main.innerHTML = html;
88
- // Clear preview panel when changing directories
89
- const preview = document.getElementById('explorer-preview');
90
- if (preview) preview.innerHTML = '';
91
- if (pushUrl) updateExplorerUrl(currentExplorerPath, true);
92
- }
93
- // Attach click handlers
94
- document.querySelectorAll('.explorer-link').forEach(link => {
95
- link.onclick = function(e) {
96
- e.preventDefault();
97
- const p = this.getAttribute('data-path');
98
- // If file, show preview; if dir, update explorer
99
- if (this.classList.contains('file-link')) {
100
- const preview = document.getElementById('explorer-preview');
101
- if (preview) {
102
- preview.innerHTML = `<div class='spinner' style='display:inline-block;vertical-align:middle;'></div> <span style='vertical-align:middle;'>Loading file...</span>`;
103
- }
104
- fetch(`/api/explorer/${encodeURIComponent(p)}`)
105
- .then(resp => resp.json())
106
- .then(fileData => {
107
- if (preview && fileData.type === 'file') {
108
- preview.innerHTML = `<h3>File: ${fileData.path}</h3><pre class='explorer-file'>${escapeHtml(fileData.content)}</pre>`;
109
- }
110
- });
111
- } else {
112
- console.log('[DEBUG] explorer-link click: navigating to dir', p);
113
- renderExplorer(p, true);
114
- }
115
- };
116
- });
117
- });
118
- }
119
-
120
- function escapeHtml(text) {
121
- const map = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;' };
122
- return text.replace(/[&<>"']'/g, m => map[m]);
123
- }
124
-
125
- function getExplorerPathFromLocation() {
126
- let path = window.location.pathname;
127
- // Remove leading slash
128
- if (path.startsWith('/')) path = path.slice(1);
129
- // If empty, root, or dot, treat as '.'
130
- if (!path || path === '' || path === '.' || path === '/') return '.';
131
- // Remove trailing slashes
132
- path = path.replace(/\/+$/g, '');
133
- console.log('[DEBUG] getExplorerPathFromLocation: extracted', path, 'from', window.location.pathname);
134
- return path;
135
- }
136
-
137
- document.addEventListener('DOMContentLoaded', function() {
138
- const initialPath = getExplorerPathFromLocation();
139
- console.log('[DEBUG] DOMContentLoaded: initialPath', initialPath);
140
- currentExplorerPath = initialPath; // Ensure currentExplorerPath matches URL
141
- setExplorerView(localStorage.getItem('explorerView') || 'list');
142
- renderExplorer(initialPath, false);
143
- document.getElementById('view-list').onclick = function() {
144
- setExplorerView('list');
145
- renderExplorer(currentExplorerPath, false);
146
- };
147
- document.getElementById('view-icons').onclick = function() {
148
- setExplorerView('icons');
149
- renderExplorer(currentExplorerPath, false);
150
- };
151
- });
152
-
153
- window.addEventListener('popstate', function(event) {
154
- const path = (event.state && event.state.explorerPath) || getExplorerPathFromLocation();
155
- console.log('[DEBUG] popstate: path', path, 'event.state:', event.state);
156
- renderExplorer(path, false);
157
- });
@@ -1,135 +0,0 @@
1
- // Quick Open (Ctrl+P) modal logic for TermWeb
2
- (function() {
3
- let allFiles = [];
4
- let modal = document.getElementById('quickopen-modal');
5
- let input = document.getElementById('quickopen-input');
6
- let results = document.getElementById('quickopen-results');
7
- let selectedIdx = -1;
8
- let loaded = false;
9
-
10
- // Recursively fetch all file paths from /api/explorer/
11
- async function fetchAllFiles(path = '.') {
12
- let files = [];
13
- try {
14
- let resp = await fetch(`/api/explorer/${encodeURIComponent(path)}`);
15
- let data = await resp.json();
16
- if (data.type === 'dir' && data.entries) {
17
- for (const entry of data.entries) {
18
- const entryPath = path === '.' ? entry.name : path + '/' + entry.name;
19
- if (entry.is_dir) {
20
- files = files.concat(await fetchAllFiles(entryPath));
21
- } else {
22
- files.push(entryPath);
23
- }
24
- }
25
- }
26
- } catch (e) { console.error('QuickOpen fetch error', e); }
27
- return files;
28
- }
29
-
30
- async function ensureLoaded() {
31
- if (!loaded) {
32
- allFiles = await fetchAllFiles('.');
33
- loaded = true;
34
- }
35
- }
36
-
37
- function showModal() {
38
- modal.style.display = 'flex';
39
- input.value = '';
40
- results.innerHTML = '';
41
- selectedIdx = -1;
42
- setTimeout(() => input.focus(), 50);
43
- }
44
- function hideModal() {
45
- modal.style.display = 'none';
46
- }
47
-
48
- function renderResults(filtered) {
49
- results.innerHTML = '';
50
- filtered.slice(0, 15).forEach((file, idx) => {
51
- let li = document.createElement('li');
52
- li.textContent = file;
53
- li.tabIndex = -1;
54
- li.className = 'quickopen-result' + (idx === selectedIdx ? ' selected' : '');
55
- li.onclick = () => openFile(file);
56
- results.appendChild(li);
57
- });
58
- }
59
-
60
- // Improved openFile: poll for file link and click when ready
61
- function openFile(path) {
62
- hideModal();
63
- window.renderExplorer && window.renderExplorer(getParentPath(path), false);
64
- const maxAttempts = 20; // 20 x 100ms = 2s max
65
- let attempts = 0;
66
- function tryClickFile() {
67
- let fileLinks = document.querySelectorAll(`a.file-link[data-path='${path}']`);
68
- if (fileLinks.length) {
69
- fileLinks[0].click();
70
- // Optionally, add a highlight class
71
- fileLinks[0].classList.add('quickopen-selected');
72
- } else if (attempts < maxAttempts) {
73
- attempts++;
74
- setTimeout(tryClickFile, 100);
75
- }
76
- }
77
- tryClickFile();
78
- }
79
-
80
- input.addEventListener('input', async function() {
81
- await ensureLoaded();
82
- let q = input.value.trim().toLowerCase();
83
- let filtered = allFiles.filter(f => f.toLowerCase().includes(q));
84
- selectedIdx = -1;
85
- renderResults(filtered);
86
- });
87
- input.addEventListener('keydown', function(e) {
88
- let items = results.querySelectorAll('li');
89
- if (e.key === 'ArrowDown') {
90
- selectedIdx = Math.min(selectedIdx + 1, items.length - 1);
91
- renderResults(Array.from(items).map(li => li.textContent));
92
- e.preventDefault();
93
- } else if (e.key === 'ArrowUp') {
94
- selectedIdx = Math.max(selectedIdx - 1, 0);
95
- renderResults(Array.from(items).map(li => li.textContent));
96
- e.preventDefault();
97
- } else if (e.key === 'Enter') {
98
- if (selectedIdx >= 0 && items[selectedIdx]) {
99
- openFile(items[selectedIdx].textContent);
100
- } else if (items.length === 1) {
101
- openFile(items[0].textContent);
102
- }
103
- } else if (e.key === 'Escape') {
104
- hideModal();
105
- }
106
- });
107
-
108
- document.addEventListener('keydown', function(e) {
109
- if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'p' && !e.shiftKey && !e.altKey) {
110
- showModal();
111
- e.preventDefault();
112
- } else if (e.key === 'Escape' && modal.style.display === 'flex') {
113
- hideModal();
114
- }
115
- });
116
- modal.addEventListener('mousedown', function(e) {
117
- if (e.target === modal) hideModal();
118
- });
119
-
120
- // Helper: get parent path
121
- function getParentPath(path) {
122
- path = (path || '').replace(/\\/g, '/').replace(/^\/+/g, '').replace(/\/+$/g, '');
123
- if (!path || path === '.' || path === '') return '.';
124
- const parts = path.split('/');
125
- if (parts.length <= 1) return '.';
126
- parts.pop();
127
- const parent = parts.join('/');
128
- return parent === '' ? '.' : parent;
129
- }
130
-
131
- // Style for selected result and quickopen-selected file link
132
- let style = document.createElement('style');
133
- style.textContent = `.quickopen-result.selected { background:#333; color:#8be9fd; } .quickopen-result { padding:0.3em 0.7em; cursor:pointer; border-radius:0.3em; } .quickopen-result:hover { background:#222; } a.file-link.quickopen-selected { background:#444 !important; color:#8be9fd !important; border-radius:0.3em; }`;
134
- document.head.appendChild(style);
135
- })();
@@ -1,125 +0,0 @@
1
- // Quick Open (Ctrl+P) modal logic for TermWeb
2
- (function() {
3
- let allFiles = [];
4
- let modal = document.getElementById('quickopen-modal');
5
- let input = document.getElementById('quickopen-input');
6
- let results = document.getElementById('quickopen-results');
7
- let selectedIdx = -1;
8
- let loaded = false;
9
-
10
- // Recursively fetch all file paths from /api/explorer/
11
- async function fetchAllFiles(path = '.') {
12
- let files = [];
13
- try {
14
- let resp = await fetch(`/api/explorer/${encodeURIComponent(path)}`);
15
- let data = await resp.json();
16
- if (data.type === 'dir' && data.entries) {
17
- for (const entry of data.entries) {
18
- const entryPath = path === '.' ? entry.name : path + '/' + entry.name;
19
- if (entry.is_dir) {
20
- files = files.concat(await fetchAllFiles(entryPath));
21
- } else {
22
- files.push(entryPath);
23
- }
24
- }
25
- }
26
- } catch (e) { console.error('QuickOpen fetch error', e); }
27
- return files;
28
- }
29
-
30
- async function ensureLoaded() {
31
- if (!loaded) {
32
- allFiles = await fetchAllFiles('.');
33
- loaded = true;
34
- }
35
- }
36
-
37
- function showModal() {
38
- modal.style.display = 'flex';
39
- input.value = '';
40
- results.innerHTML = '';
41
- selectedIdx = -1;
42
- setTimeout(() => input.focus(), 50);
43
- }
44
- function hideModal() {
45
- modal.style.display = 'none';
46
- }
47
-
48
- function renderResults(filtered) {
49
- results.innerHTML = '';
50
- filtered.slice(0, 15).forEach((file, idx) => {
51
- let li = document.createElement('li');
52
- li.textContent = file;
53
- li.tabIndex = -1;
54
- li.className = 'quickopen-result' + (idx === selectedIdx ? ' selected' : '');
55
- li.onclick = () => openFile(file);
56
- results.appendChild(li);
57
- });
58
- }
59
-
60
- function openFile(path) {
61
- hideModal();
62
- // Simulate clicking the file in explorer
63
- window.renderExplorer && window.renderExplorer(getParentPath(path), false);
64
- setTimeout(() => {
65
- let fileLinks = document.querySelectorAll(`a.file-link[data-path='${path}']`);
66
- if (fileLinks.length) fileLinks[0].click();
67
- }, 250);
68
- }
69
-
70
- input.addEventListener('input', async function() {
71
- await ensureLoaded();
72
- let q = input.value.trim().toLowerCase();
73
- let filtered = allFiles.filter(f => f.toLowerCase().includes(q));
74
- selectedIdx = -1;
75
- renderResults(filtered);
76
- });
77
- input.addEventListener('keydown', function(e) {
78
- let items = results.querySelectorAll('li');
79
- if (e.key === 'ArrowDown') {
80
- selectedIdx = Math.min(selectedIdx + 1, items.length - 1);
81
- renderResults(Array.from(items).map(li => li.textContent));
82
- e.preventDefault();
83
- } else if (e.key === 'ArrowUp') {
84
- selectedIdx = Math.max(selectedIdx - 1, 0);
85
- renderResults(Array.from(items).map(li => li.textContent));
86
- e.preventDefault();
87
- } else if (e.key === 'Enter') {
88
- if (selectedIdx >= 0 && items[selectedIdx]) {
89
- openFile(items[selectedIdx].textContent);
90
- } else if (items.length === 1) {
91
- openFile(items[0].textContent);
92
- }
93
- } else if (e.key === 'Escape') {
94
- hideModal();
95
- }
96
- });
97
-
98
- document.addEventListener('keydown', function(e) {
99
- if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'p' && !e.shiftKey && !e.altKey) {
100
- showModal();
101
- e.preventDefault();
102
- } else if (e.key === 'Escape' && modal.style.display === 'flex') {
103
- hideModal();
104
- }
105
- });
106
- modal.addEventListener('mousedown', function(e) {
107
- if (e.target === modal) hideModal();
108
- });
109
-
110
- // Helper: get parent path
111
- function getParentPath(path) {
112
- path = (path || '').replace(/\\/g, '/').replace(/^\/+|\/+$/g, '');
113
- if (!path || path === '.' || path === '') return '.';
114
- const parts = path.split('/');
115
- if (parts.length <= 1) return '.';
116
- parts.pop();
117
- const parent = parts.join('/');
118
- return parent === '' ? '.' : parent;
119
- }
120
-
121
- // Style for selected result
122
- let style = document.createElement('style');
123
- style.textContent = `.quickopen-result.selected { background:#333; color:#8be9fd; } .quickopen-result { padding:0.3em 0.7em; cursor:pointer; border-radius:0.3em; } .quickopen-result:hover { background:#222; }`;
124
- document.head.appendChild(style);
125
- })();
@@ -1,44 +0,0 @@
1
- import io
2
- from rich.console import Console
3
- from janito.rich_utils import RichPrinter
4
-
5
-
6
- def test_print_info(capsys=None):
7
- buf = io.StringIO()
8
- printer = RichPrinter(
9
- console=Console(file=buf, force_terminal=True, color_system=None)
10
- )
11
- printer.print_info("info message")
12
- output = buf.getvalue()
13
- assert "info message" in output
14
- assert "cyan" in output or output # Style is present if rich renders ANSI
15
-
16
-
17
- def test_print_error():
18
- buf = io.StringIO()
19
- printer = RichPrinter(
20
- console=Console(file=buf, force_terminal=True, color_system=None)
21
- )
22
- printer.print_error("error message")
23
- output = buf.getvalue()
24
- assert "error message" in output
25
-
26
-
27
- def test_print_warning():
28
- buf = io.StringIO()
29
- printer = RichPrinter(
30
- console=Console(file=buf, force_terminal=True, color_system=None)
31
- )
32
- printer.print_warning("warning message")
33
- output = buf.getvalue()
34
- assert "warning message" in output
35
-
36
-
37
- def test_print_magenta():
38
- buf = io.StringIO()
39
- printer = RichPrinter(
40
- console=Console(file=buf, force_terminal=True, color_system=None)
41
- )
42
- printer.print_magenta("magenta message")
43
- output = buf.getvalue()
44
- assert "magenta message" in output
janito/web/__init__.py DELETED
File without changes
janito/web/__main__.py DELETED
@@ -1,25 +0,0 @@
1
- import sys
2
- from . import app
3
-
4
-
5
- def main():
6
- import os
7
-
8
- # Ensure PYTHONUTF8 is set for consistent UTF-8 behavior
9
- if os.environ.get("PYTHONUTF8") != "1":
10
- os.environ["PYTHONUTF8"] = "1"
11
- if os.name == "nt" and sys.argv[0]:
12
- print("[info] Restarting web server with PYTHONUTF8=1 for UTF-8 support...")
13
- os.execvpe(sys.executable, [sys.executable] + sys.argv, os.environ)
14
- port = 8000
15
- if len(sys.argv) > 1:
16
- try:
17
- port = int(sys.argv[1])
18
- except ValueError:
19
- print(f"Invalid port number: {sys.argv[1]}")
20
- sys.exit(1)
21
- app.app.run(host="0.0.0.0", port=port, debug=True)
22
-
23
-
24
- if __name__ == "__main__":
25
- main()
janito/web/app.py DELETED
@@ -1,145 +0,0 @@
1
- from flask import (
2
- Flask,
3
- request,
4
- send_from_directory,
5
- jsonify,
6
- render_template,
7
- )
8
- import json
9
- from janito.agent.profile_manager import AgentProfileManager
10
- import os
11
-
12
- from janito.agent.runtime_config import unified_config, runtime_config
13
-
14
- # Render system prompt from config
15
- role = unified_config.get("role", "software engineer")
16
- system_prompt_template_override = unified_config.get("system_prompt_template")
17
- if system_prompt_template_override:
18
- system_prompt_template = system_prompt_template_override
19
- else:
20
- profile_manager = AgentProfileManager(
21
- api_key=unified_config.get("api_key"),
22
- model=unified_config.get("model"),
23
- role=role,
24
- profile_name="base",
25
- interaction_mode=unified_config.get("interaction_mode", "prompt"),
26
- verbose_tools=runtime_config.get("verbose_tools", False),
27
- base_url=unified_config.get("base_url", None),
28
- azure_openai_api_version=unified_config.get(
29
- "azure_openai_api_version", "2023-05-15"
30
- ),
31
- use_azure_openai=unified_config.get("use_azure_openai", False),
32
- )
33
- system_prompt_template = profile_manager.system_prompt_template
34
-
35
- app = Flask(
36
- __name__,
37
- static_url_path="/static",
38
- static_folder=os.path.join(os.path.dirname(__file__), "static"),
39
- )
40
-
41
- # Secret key for session management
42
- app.secret_key = "replace_with_a_secure_random_secret_key"
43
-
44
- # Path for persistent conversation storage
45
- conversation_file = os.path.expanduser("~/.janito/last_conversation_web.json")
46
-
47
- # Initially no conversation loaded
48
- conversation = None
49
-
50
- # Instantiate the Agent with config-driven parameters (no tool_handler)
51
- agent = profile_manager.agent
52
-
53
-
54
- @app.route("/get_config")
55
- def get_config():
56
- # Expose full config for the web app: defaults, effective, runtime (mask api_key)
57
- from janito.agent.runtime_config import (
58
- unified_config,
59
- ) # Kept here: avoids circular import at module level
60
- from janito.agent.config_defaults import CONFIG_DEFAULTS
61
-
62
- # Start with defaults
63
- config = dict(CONFIG_DEFAULTS)
64
- # Overlay effective config
65
- config.update(unified_config.effective_cfg.all())
66
- # Overlay runtime config (highest priority)
67
- config.update(unified_config.runtime_cfg.all())
68
- api_key = config.get("api_key")
69
- if api_key:
70
- config["api_key"] = (
71
- api_key[:4] + "..." + api_key[-4:] if len(api_key) > 8 else "***"
72
- )
73
- return jsonify(config)
74
-
75
-
76
- @app.route("/set_config", methods=["POST"])
77
- def set_config():
78
- from janito.agent.runtime_config import runtime_config
79
- from janito.agent.config import CONFIG_OPTIONS
80
- from janito.agent.config_defaults import CONFIG_DEFAULTS
81
-
82
- data = request.get_json()
83
- key = data.get("key")
84
- value = data.get("value")
85
- if key not in CONFIG_OPTIONS:
86
- return (
87
- jsonify({"status": "error", "message": f"Invalid config key: {key}"}),
88
- 400,
89
- )
90
- # Type coercion based on defaults
91
- default = CONFIG_DEFAULTS.get(key)
92
- if default is not None and value is not None:
93
- try:
94
- if isinstance(default, bool):
95
- value = bool(value)
96
- elif isinstance(default, int):
97
- value = int(value)
98
- elif isinstance(default, float):
99
- value = float(value)
100
- # else: leave as string or None
101
- except Exception as e:
102
- return (
103
- jsonify(
104
- {"status": "error", "message": f"Invalid value type for {key}: {e}"}
105
- ),
106
- 400,
107
- )
108
- runtime_config.set(key, value)
109
- # Mask api_key in response
110
- resp_value = value
111
- if key == "api_key" and value:
112
- resp_value = value[:4] + "..." + value[-4:] if len(value) > 8 else "***"
113
- return jsonify({"status": "ok", "key": key, "value": resp_value})
114
-
115
-
116
- @app.route("/favicon.ico")
117
- def favicon():
118
- return send_from_directory(
119
- os.path.join(app.root_path, "static"),
120
- "favicon.ico",
121
- mimetype="image/vnd.microsoft.icon",
122
- )
123
-
124
-
125
- @app.route("/")
126
- def index():
127
- return render_template("index.html")
128
-
129
-
130
- @app.route("/load_conversation")
131
- def load_conversation():
132
- global conversation
133
- try:
134
- with open(conversation_file, "r", encoding="utf-8") as f:
135
- conversation = json.load(f)
136
- except (FileNotFoundError, json.JSONDecodeError):
137
- conversation = []
138
- return jsonify({"status": "ok", "conversation": conversation})
139
-
140
-
141
- @app.route("/new_conversation", methods=["POST"])
142
- def new_conversation():
143
- global conversation
144
- conversation = []
145
- return jsonify({"status": "ok"})