janito 1.14.3__py3-none-any.whl → 2.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (283) 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/tools/adapters/local/open_html_in_browser.py +34 -0
  131. janito/{agent/tools → tools/adapters/local}/open_url.py +7 -5
  132. janito/tools/adapters/local/python_code_run.py +165 -0
  133. janito/tools/adapters/local/python_command_run.py +163 -0
  134. janito/tools/adapters/local/python_file_run.py +162 -0
  135. janito/{agent/tools → tools/adapters/local}/remove_directory.py +15 -9
  136. janito/{agent/tools → tools/adapters/local}/remove_file.py +17 -14
  137. janito/{agent/tools → tools/adapters/local}/replace_text_in_file.py +27 -22
  138. janito/tools/adapters/local/run_bash_command.py +176 -0
  139. janito/tools/adapters/local/run_powershell_command.py +219 -0
  140. janito/{agent/tools → tools/adapters/local}/search_text/core.py +32 -12
  141. janito/{agent/tools → tools/adapters/local}/search_text/match_lines.py +13 -4
  142. janito/{agent/tools → tools/adapters/local}/search_text/pattern_utils.py +12 -4
  143. janito/{agent/tools → tools/adapters/local}/search_text/traverse_directory.py +15 -2
  144. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/core.py +12 -11
  145. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/css_validator.py +1 -1
  146. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/html_validator.py +1 -1
  147. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/js_validator.py +1 -1
  148. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/json_validator.py +1 -1
  149. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/markdown_validator.py +1 -1
  150. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/ps1_validator.py +1 -1
  151. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/python_validator.py +1 -1
  152. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/xml_validator.py +1 -1
  153. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/yaml_validator.py +1 -1
  154. janito/{agent/tools/get_lines.py → tools/adapters/local/view_file.py} +45 -27
  155. janito/tools/inspect_registry.py +17 -0
  156. janito/tools/tool_base.py +105 -0
  157. janito/tools/tool_events.py +58 -0
  158. janito/tools/tool_run_exception.py +12 -0
  159. janito/{agent → tools}/tool_use_tracker.py +2 -4
  160. janito/{agent/tools_utils/utils.py → tools/tool_utils.py} +18 -9
  161. janito/tools/tools_adapter.py +207 -0
  162. janito/tools/tools_schema.py +104 -0
  163. janito/utils.py +11 -0
  164. janito/version.py +4 -0
  165. janito-2.0.1.dist-info/METADATA +232 -0
  166. janito-2.0.1.dist-info/RECORD +181 -0
  167. janito/agent/__init__.py +0 -0
  168. janito/agent/api_exceptions.py +0 -4
  169. janito/agent/config.py +0 -147
  170. janito/agent/config_defaults.py +0 -12
  171. janito/agent/config_utils.py +0 -0
  172. janito/agent/content_handler.py +0 -0
  173. janito/agent/conversation.py +0 -238
  174. janito/agent/conversation_api.py +0 -306
  175. janito/agent/conversation_exceptions.py +0 -18
  176. janito/agent/conversation_tool_calls.py +0 -39
  177. janito/agent/conversation_ui.py +0 -17
  178. janito/agent/event.py +0 -24
  179. janito/agent/event_dispatcher.py +0 -24
  180. janito/agent/event_handler_protocol.py +0 -5
  181. janito/agent/event_system.py +0 -15
  182. janito/agent/llm_conversation_history.py +0 -82
  183. janito/agent/message_handler.py +0 -20
  184. janito/agent/message_handler_protocol.py +0 -5
  185. janito/agent/openai_client.py +0 -149
  186. janito/agent/openai_schema_generator.py +0 -187
  187. janito/agent/profile_manager.py +0 -96
  188. janito/agent/queued_message_handler.py +0 -50
  189. janito/agent/rich_live.py +0 -32
  190. janito/agent/rich_message_handler.py +0 -115
  191. janito/agent/runtime_config.py +0 -36
  192. janito/agent/test_handler_protocols.py +0 -47
  193. janito/agent/test_openai_schema_generator.py +0 -93
  194. janito/agent/tests/__init__.py +0 -1
  195. janito/agent/tool_base.py +0 -63
  196. janito/agent/tool_executor.py +0 -122
  197. janito/agent/tool_registry.py +0 -49
  198. janito/agent/tools/__init__.py +0 -47
  199. janito/agent/tools/create_file.py +0 -59
  200. janito/agent/tools/delete_text_in_file.py +0 -97
  201. janito/agent/tools/find_files.py +0 -106
  202. janito/agent/tools/get_file_outline/core.py +0 -81
  203. janito/agent/tools/present_choices.py +0 -64
  204. janito/agent/tools/python_command_runner.py +0 -201
  205. janito/agent/tools/python_file_runner.py +0 -199
  206. janito/agent/tools/python_stdin_runner.py +0 -208
  207. janito/agent/tools/replace_file.py +0 -72
  208. janito/agent/tools/run_bash_command.py +0 -218
  209. janito/agent/tools/run_powershell_command.py +0 -251
  210. janito/agent/tools_utils/__init__.py +0 -1
  211. janito/agent/tools_utils/action_type.py +0 -7
  212. janito/agent/tools_utils/test_gitignore_utils.py +0 -46
  213. janito/cli/_livereload_log_utils.py +0 -13
  214. janito/cli/_print_config.py +0 -96
  215. janito/cli/_termweb_log_utils.py +0 -17
  216. janito/cli/_utils.py +0 -9
  217. janito/cli/arg_parser.py +0 -272
  218. janito/cli/cli_main.py +0 -281
  219. janito/cli/config_commands.py +0 -211
  220. janito/cli/config_runner.py +0 -35
  221. janito/cli/formatting_runner.py +0 -12
  222. janito/cli/livereload_starter.py +0 -60
  223. janito/cli/logging_setup.py +0 -38
  224. janito/cli/one_shot.py +0 -80
  225. janito/livereload/app.py +0 -25
  226. janito/rich_utils.py +0 -59
  227. janito/shell/__init__.py +0 -0
  228. janito/shell/commands/__init__.py +0 -61
  229. janito/shell/commands/config.py +0 -22
  230. janito/shell/commands/edit.py +0 -24
  231. janito/shell/commands/history_view.py +0 -18
  232. janito/shell/commands/lang.py +0 -19
  233. janito/shell/commands/livelogs.py +0 -42
  234. janito/shell/commands/prompt.py +0 -62
  235. janito/shell/commands/termweb_log.py +0 -94
  236. janito/shell/commands/tools.py +0 -26
  237. janito/shell/commands/track.py +0 -36
  238. janito/shell/main.py +0 -326
  239. janito/shell/prompt/load_prompt.py +0 -57
  240. janito/shell/prompt/session_setup.py +0 -57
  241. janito/shell/session/config.py +0 -109
  242. janito/shell/session/history.py +0 -0
  243. janito/shell/ui/interactive.py +0 -226
  244. janito/termweb/static/editor.css +0 -158
  245. janito/termweb/static/editor.css.bak +0 -145
  246. janito/termweb/static/editor.html +0 -46
  247. janito/termweb/static/editor.html.bak +0 -46
  248. janito/termweb/static/editor.js +0 -265
  249. janito/termweb/static/editor.js.bak +0 -259
  250. janito/termweb/static/explorer.html.bak +0 -59
  251. janito/termweb/static/favicon.ico +0 -0
  252. janito/termweb/static/favicon.ico.bak +0 -0
  253. janito/termweb/static/index.html +0 -53
  254. janito/termweb/static/index.html.bak +0 -54
  255. janito/termweb/static/index.html.bak.bak +0 -175
  256. janito/termweb/static/landing.html.bak +0 -36
  257. janito/termweb/static/termicon.svg +0 -1
  258. janito/termweb/static/termweb.css +0 -214
  259. janito/termweb/static/termweb.css.bak +0 -237
  260. janito/termweb/static/termweb.js +0 -162
  261. janito/termweb/static/termweb.js.bak +0 -168
  262. janito/termweb/static/termweb.js.bak.bak +0 -157
  263. janito/termweb/static/termweb_quickopen.js +0 -135
  264. janito/termweb/static/termweb_quickopen.js.bak +0 -125
  265. janito/tests/test_rich_utils.py +0 -44
  266. janito/web/__init__.py +0 -0
  267. janito/web/__main__.py +0 -25
  268. janito/web/app.py +0 -145
  269. janito-1.14.3.dist-info/METADATA +0 -313
  270. janito-1.14.3.dist-info/RECORD +0 -162
  271. janito-1.14.3.dist-info/licenses/LICENSE +0 -21
  272. /janito/{shell → cli/chat_mode/shell}/input_history.py +0 -0
  273. /janito/{shell/commands/session.py → cli/chat_mode/shell/session/history.py} +0 -0
  274. /janito/{agent/tools_utils/formatting.py → formatting.py} +0 -0
  275. /janito/{agent/tools_utils/gitignore_utils.py → gitignore_utils.py} +0 -0
  276. /janito/{agent/platform_discovery.py → platform_discovery.py} +0 -0
  277. /janito/{agent/tools → tools/adapters/local}/get_file_outline/__init__.py +0 -0
  278. /janito/{agent/tools → tools/adapters/local}/get_file_outline/markdown_outline.py +0 -0
  279. /janito/{agent/tools → tools/adapters/local}/search_text/__init__.py +0 -0
  280. /janito/{agent/tools → tools/adapters/local}/validate_file_syntax/__init__.py +0 -0
  281. {janito-1.14.3.dist-info → janito-2.0.1.dist-info}/WHEEL +0 -0
  282. {janito-1.14.3.dist-info → janito-2.0.1.dist-info}/entry_points.txt +0 -0
  283. {janito-1.14.3.dist-info → janito-2.0.1.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"})