janito 1.14.3__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.
- janito/__init__.py +6 -1
- janito/__main__.py +1 -1
- janito/agent/setup_agent.py +139 -0
- janito/agent/templates/profiles/{system_prompt_template_base.txt.j2 → system_prompt_template_main.txt.j2} +1 -1
- janito/cli/__init__.py +9 -0
- janito/cli/chat_mode/bindings.py +37 -0
- janito/cli/chat_mode/chat_entry.py +23 -0
- janito/cli/chat_mode/prompt_style.py +19 -0
- janito/cli/chat_mode/session.py +272 -0
- janito/{shell/prompt/completer.py → cli/chat_mode/shell/autocomplete.py} +7 -6
- janito/cli/chat_mode/shell/commands/__init__.py +55 -0
- janito/cli/chat_mode/shell/commands/base.py +9 -0
- janito/cli/chat_mode/shell/commands/clear.py +12 -0
- janito/{shell → cli/chat_mode/shell}/commands/conversation_restart.py +34 -30
- janito/cli/chat_mode/shell/commands/edit.py +25 -0
- janito/cli/chat_mode/shell/commands/help.py +16 -0
- janito/cli/chat_mode/shell/commands/history_view.py +93 -0
- janito/cli/chat_mode/shell/commands/lang.py +25 -0
- janito/cli/chat_mode/shell/commands/last.py +137 -0
- janito/cli/chat_mode/shell/commands/livelogs.py +49 -0
- janito/cli/chat_mode/shell/commands/multi.py +51 -0
- janito/cli/chat_mode/shell/commands/prompt.py +64 -0
- janito/cli/chat_mode/shell/commands/role.py +36 -0
- janito/cli/chat_mode/shell/commands/session.py +40 -0
- janito/{shell → cli/chat_mode/shell}/commands/session_control.py +2 -2
- janito/cli/chat_mode/shell/commands/termweb_log.py +92 -0
- janito/cli/chat_mode/shell/commands/tools.py +32 -0
- janito/{shell → cli/chat_mode/shell}/commands/utility.py +4 -7
- janito/{shell → cli/chat_mode/shell}/commands/verbose.py +5 -5
- janito/cli/chat_mode/shell/session/__init__.py +1 -0
- janito/{shell → cli/chat_mode/shell}/session/manager.py +9 -1
- janito/cli/chat_mode/toolbar.py +90 -0
- janito/cli/cli_commands/list_models.py +35 -0
- janito/cli/cli_commands/list_providers.py +9 -0
- janito/cli/cli_commands/list_tools.py +53 -0
- janito/cli/cli_commands/model_selection.py +50 -0
- janito/cli/cli_commands/model_utils.py +84 -0
- janito/cli/cli_commands/set_api_key.py +19 -0
- janito/cli/cli_commands/show_config.py +51 -0
- janito/cli/cli_commands/show_system_prompt.py +62 -0
- janito/cli/config.py +28 -0
- janito/cli/console.py +3 -0
- janito/cli/core/__init__.py +4 -0
- janito/cli/core/event_logger.py +59 -0
- janito/cli/core/getters.py +31 -0
- janito/cli/core/runner.py +141 -0
- janito/cli/core/setters.py +174 -0
- janito/cli/core/unsetters.py +54 -0
- janito/cli/main.py +8 -196
- janito/cli/main_cli.py +312 -0
- janito/cli/prompt_core.py +230 -0
- janito/cli/prompt_handler.py +6 -0
- janito/cli/rich_terminal_reporter.py +101 -0
- janito/cli/single_shot_mode/__init__.py +6 -0
- janito/cli/single_shot_mode/handler.py +137 -0
- janito/cli/termweb_starter.py +73 -24
- janito/cli/utils.py +25 -0
- janito/cli/verbose_output.py +196 -0
- janito/config.py +5 -0
- janito/config_manager.py +110 -0
- janito/conversation_history.py +30 -0
- janito/{agent/tools_utils/dir_walk_utils.py → dir_walk_utils.py} +3 -2
- janito/driver_events.py +98 -0
- janito/drivers/anthropic/driver.py +113 -0
- janito/drivers/azure_openai/driver.py +36 -0
- janito/drivers/driver_registry.py +33 -0
- janito/drivers/google_genai/driver.py +54 -0
- janito/drivers/google_genai/schema_generator.py +67 -0
- janito/drivers/mistralai/driver.py +41 -0
- janito/drivers/openai/driver.py +334 -0
- janito/event_bus/__init__.py +2 -0
- janito/event_bus/bus.py +68 -0
- janito/event_bus/event.py +15 -0
- janito/event_bus/handler.py +31 -0
- janito/event_bus/queue_bus.py +57 -0
- janito/exceptions.py +23 -0
- janito/formatting_token.py +54 -0
- janito/i18n/pt.py +1 -0
- janito/llm/__init__.py +5 -0
- janito/llm/agent.py +443 -0
- janito/llm/auth.py +62 -0
- janito/llm/driver.py +239 -0
- janito/llm/driver_config.py +34 -0
- janito/llm/driver_config_builder.py +34 -0
- janito/llm/driver_input.py +12 -0
- janito/llm/message_parts.py +60 -0
- janito/llm/model.py +38 -0
- janito/llm/provider.py +187 -0
- janito/perf_singleton.py +3 -0
- janito/performance_collector.py +167 -0
- janito/provider_config.py +98 -0
- janito/provider_registry.py +152 -0
- janito/providers/__init__.py +7 -0
- janito/providers/anthropic/model_info.py +22 -0
- janito/providers/anthropic/provider.py +65 -0
- janito/providers/azure_openai/model_info.py +15 -0
- janito/providers/azure_openai/provider.py +72 -0
- janito/providers/deepseek/__init__.py +1 -0
- janito/providers/deepseek/model_info.py +16 -0
- janito/providers/deepseek/provider.py +91 -0
- janito/providers/google/__init__.py +1 -0
- janito/providers/google/model_info.py +40 -0
- janito/providers/google/provider.py +69 -0
- janito/providers/mistralai/model_info.py +37 -0
- janito/providers/mistralai/provider.py +69 -0
- janito/providers/openai/__init__.py +1 -0
- janito/providers/openai/model_info.py +137 -0
- janito/providers/openai/provider.py +107 -0
- janito/providers/openai/schema_generator.py +63 -0
- janito/providers/provider_static_info.py +21 -0
- janito/providers/registry.py +26 -0
- janito/report_events.py +38 -0
- janito/termweb/app.py +1 -1
- janito/tools/__init__.py +16 -0
- janito/tools/adapters/__init__.py +1 -0
- janito/tools/adapters/local/__init__.py +54 -0
- janito/tools/adapters/local/adapter.py +92 -0
- janito/{agent/tools → tools/adapters/local}/ask_user.py +30 -13
- janito/tools/adapters/local/copy_file.py +84 -0
- janito/{agent/tools → tools/adapters/local}/create_directory.py +11 -10
- janito/tools/adapters/local/create_file.py +82 -0
- janito/tools/adapters/local/delete_text_in_file.py +136 -0
- janito/{agent/tools → tools/adapters/local}/fetch_url.py +18 -19
- janito/tools/adapters/local/find_files.py +140 -0
- janito/tools/adapters/local/get_file_outline/core.py +151 -0
- janito/{agent/tools → tools/adapters/local}/get_file_outline/python_outline.py +125 -0
- janito/tools/adapters/local/get_file_outline/python_outline_v2.py +156 -0
- janito/{agent/tools → tools/adapters/local}/get_file_outline/search_outline.py +12 -7
- janito/{agent/tools → tools/adapters/local}/move_file.py +13 -9
- janito/{agent/tools → tools/adapters/local}/open_url.py +7 -5
- janito/tools/adapters/local/python_code_run.py +165 -0
- janito/tools/adapters/local/python_command_run.py +163 -0
- janito/tools/adapters/local/python_file_run.py +162 -0
- janito/{agent/tools → tools/adapters/local}/remove_directory.py +15 -9
- janito/{agent/tools → tools/adapters/local}/remove_file.py +17 -14
- janito/{agent/tools → tools/adapters/local}/replace_text_in_file.py +27 -22
- janito/tools/adapters/local/run_bash_command.py +176 -0
- janito/tools/adapters/local/run_powershell_command.py +219 -0
- janito/{agent/tools → tools/adapters/local}/search_text/core.py +32 -12
- janito/{agent/tools → tools/adapters/local}/search_text/match_lines.py +13 -4
- janito/{agent/tools → tools/adapters/local}/search_text/pattern_utils.py +12 -4
- janito/{agent/tools → tools/adapters/local}/search_text/traverse_directory.py +15 -2
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/core.py +12 -11
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/css_validator.py +1 -1
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/html_validator.py +1 -1
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/js_validator.py +1 -1
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/json_validator.py +1 -1
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/markdown_validator.py +1 -1
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/ps1_validator.py +1 -1
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/python_validator.py +1 -1
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/xml_validator.py +1 -1
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/yaml_validator.py +1 -1
- janito/{agent/tools/get_lines.py → tools/adapters/local/view_file.py} +45 -27
- janito/tools/inspect_registry.py +17 -0
- janito/tools/tool_base.py +105 -0
- janito/tools/tool_events.py +58 -0
- janito/tools/tool_run_exception.py +12 -0
- janito/{agent → tools}/tool_use_tracker.py +2 -4
- janito/{agent/tools_utils/utils.py → tools/tool_utils.py} +18 -9
- janito/tools/tools_adapter.py +207 -0
- janito/tools/tools_schema.py +104 -0
- janito/utils.py +11 -0
- janito/version.py +4 -0
- janito-2.0.0.dist-info/METADATA +232 -0
- janito-2.0.0.dist-info/RECORD +180 -0
- janito/agent/__init__.py +0 -0
- janito/agent/api_exceptions.py +0 -4
- janito/agent/config.py +0 -147
- janito/agent/config_defaults.py +0 -12
- janito/agent/config_utils.py +0 -0
- janito/agent/content_handler.py +0 -0
- janito/agent/conversation.py +0 -238
- janito/agent/conversation_api.py +0 -306
- janito/agent/conversation_exceptions.py +0 -18
- janito/agent/conversation_tool_calls.py +0 -39
- janito/agent/conversation_ui.py +0 -17
- janito/agent/event.py +0 -24
- janito/agent/event_dispatcher.py +0 -24
- janito/agent/event_handler_protocol.py +0 -5
- janito/agent/event_system.py +0 -15
- janito/agent/llm_conversation_history.py +0 -82
- janito/agent/message_handler.py +0 -20
- janito/agent/message_handler_protocol.py +0 -5
- janito/agent/openai_client.py +0 -149
- janito/agent/openai_schema_generator.py +0 -187
- janito/agent/profile_manager.py +0 -96
- janito/agent/queued_message_handler.py +0 -50
- janito/agent/rich_live.py +0 -32
- janito/agent/rich_message_handler.py +0 -115
- janito/agent/runtime_config.py +0 -36
- janito/agent/test_handler_protocols.py +0 -47
- janito/agent/test_openai_schema_generator.py +0 -93
- janito/agent/tests/__init__.py +0 -1
- janito/agent/tool_base.py +0 -63
- janito/agent/tool_executor.py +0 -122
- janito/agent/tool_registry.py +0 -49
- janito/agent/tools/__init__.py +0 -47
- janito/agent/tools/create_file.py +0 -59
- janito/agent/tools/delete_text_in_file.py +0 -97
- janito/agent/tools/find_files.py +0 -106
- janito/agent/tools/get_file_outline/core.py +0 -81
- janito/agent/tools/present_choices.py +0 -64
- janito/agent/tools/python_command_runner.py +0 -201
- janito/agent/tools/python_file_runner.py +0 -199
- janito/agent/tools/python_stdin_runner.py +0 -208
- janito/agent/tools/replace_file.py +0 -72
- janito/agent/tools/run_bash_command.py +0 -218
- janito/agent/tools/run_powershell_command.py +0 -251
- janito/agent/tools_utils/__init__.py +0 -1
- janito/agent/tools_utils/action_type.py +0 -7
- janito/agent/tools_utils/test_gitignore_utils.py +0 -46
- janito/cli/_livereload_log_utils.py +0 -13
- janito/cli/_print_config.py +0 -96
- janito/cli/_termweb_log_utils.py +0 -17
- janito/cli/_utils.py +0 -9
- janito/cli/arg_parser.py +0 -272
- janito/cli/cli_main.py +0 -281
- janito/cli/config_commands.py +0 -211
- janito/cli/config_runner.py +0 -35
- janito/cli/formatting_runner.py +0 -12
- janito/cli/livereload_starter.py +0 -60
- janito/cli/logging_setup.py +0 -38
- janito/cli/one_shot.py +0 -80
- janito/livereload/app.py +0 -25
- janito/rich_utils.py +0 -59
- janito/shell/__init__.py +0 -0
- janito/shell/commands/__init__.py +0 -61
- janito/shell/commands/config.py +0 -22
- janito/shell/commands/edit.py +0 -24
- janito/shell/commands/history_view.py +0 -18
- janito/shell/commands/lang.py +0 -19
- janito/shell/commands/livelogs.py +0 -42
- janito/shell/commands/prompt.py +0 -62
- janito/shell/commands/termweb_log.py +0 -94
- janito/shell/commands/tools.py +0 -26
- janito/shell/commands/track.py +0 -36
- janito/shell/main.py +0 -326
- janito/shell/prompt/load_prompt.py +0 -57
- janito/shell/prompt/session_setup.py +0 -57
- janito/shell/session/config.py +0 -109
- janito/shell/session/history.py +0 -0
- janito/shell/ui/interactive.py +0 -226
- janito/termweb/static/editor.css +0 -158
- janito/termweb/static/editor.css.bak +0 -145
- janito/termweb/static/editor.html +0 -46
- janito/termweb/static/editor.html.bak +0 -46
- janito/termweb/static/editor.js +0 -265
- janito/termweb/static/editor.js.bak +0 -259
- janito/termweb/static/explorer.html.bak +0 -59
- janito/termweb/static/favicon.ico +0 -0
- janito/termweb/static/favicon.ico.bak +0 -0
- janito/termweb/static/index.html +0 -53
- janito/termweb/static/index.html.bak +0 -54
- janito/termweb/static/index.html.bak.bak +0 -175
- janito/termweb/static/landing.html.bak +0 -36
- janito/termweb/static/termicon.svg +0 -1
- janito/termweb/static/termweb.css +0 -214
- janito/termweb/static/termweb.css.bak +0 -237
- janito/termweb/static/termweb.js +0 -162
- janito/termweb/static/termweb.js.bak +0 -168
- janito/termweb/static/termweb.js.bak.bak +0 -157
- janito/termweb/static/termweb_quickopen.js +0 -135
- janito/termweb/static/termweb_quickopen.js.bak +0 -125
- janito/tests/test_rich_utils.py +0 -44
- janito/web/__init__.py +0 -0
- janito/web/__main__.py +0 -25
- janito/web/app.py +0 -145
- janito-1.14.3.dist-info/METADATA +0 -313
- janito-1.14.3.dist-info/RECORD +0 -162
- janito-1.14.3.dist-info/licenses/LICENSE +0 -21
- /janito/{shell → cli/chat_mode/shell}/input_history.py +0 -0
- /janito/{shell/commands/session.py → cli/chat_mode/shell/session/history.py} +0 -0
- /janito/{agent/tools_utils/formatting.py → formatting.py} +0 -0
- /janito/{agent/tools_utils/gitignore_utils.py → gitignore_utils.py} +0 -0
- /janito/{agent/platform_discovery.py → platform_discovery.py} +0 -0
- /janito/{agent/tools → tools/adapters/local}/get_file_outline/__init__.py +0 -0
- /janito/{agent/tools → tools/adapters/local}/get_file_outline/markdown_outline.py +0 -0
- /janito/{agent/tools → tools/adapters/local}/search_text/__init__.py +0 -0
- /janito/{agent/tools → tools/adapters/local}/validate_file_syntax/__init__.py +0 -0
- {janito-1.14.3.dist-info → janito-2.0.0.dist-info}/WHEEL +0 -0
- {janito-1.14.3.dist-info → janito-2.0.0.dist-info}/entry_points.txt +0 -0
- {janito-1.14.3.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 = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' };
|
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
|
-
})();
|
janito/tests/test_rich_utils.py
DELETED
@@ -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"})
|