janito 1.6.0__py3-none-any.whl → 1.8.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 +1 -1
- janito/agent/config.py +3 -3
- janito/agent/config_defaults.py +3 -2
- janito/agent/conversation.py +73 -27
- janito/agent/conversation_api.py +104 -4
- janito/agent/conversation_exceptions.py +6 -0
- janito/agent/conversation_tool_calls.py +17 -3
- janito/agent/event.py +24 -0
- janito/agent/event_dispatcher.py +24 -0
- janito/agent/event_handler_protocol.py +5 -0
- janito/agent/event_system.py +15 -0
- janito/agent/message_handler.py +4 -1
- janito/agent/message_handler_protocol.py +5 -0
- janito/agent/openai_client.py +5 -6
- janito/agent/openai_schema_generator.py +23 -4
- janito/agent/platform_discovery.py +90 -0
- janito/agent/profile_manager.py +34 -110
- janito/agent/queued_message_handler.py +22 -3
- janito/agent/rich_message_handler.py +3 -1
- janito/agent/templates/profiles/system_prompt_template_base.txt.j2 +14 -0
- janito/agent/templates/profiles/system_prompt_template_base_pt.txt.j2 +13 -0
- janito/agent/test_handler_protocols.py +47 -0
- janito/agent/tests/__init__.py +1 -0
- janito/agent/tool_base.py +1 -1
- janito/agent/tool_executor.py +109 -0
- janito/agent/tool_registry.py +3 -75
- janito/agent/tool_use_tracker.py +46 -0
- janito/agent/tools/__init__.py +11 -8
- janito/agent/tools/ask_user.py +26 -12
- janito/agent/tools/create_directory.py +50 -18
- janito/agent/tools/create_file.py +60 -29
- janito/agent/tools/dir_walk_utils.py +16 -0
- janito/agent/tools/fetch_url.py +10 -11
- janito/agent/tools/find_files.py +49 -40
- janito/agent/tools/get_lines.py +60 -25
- janito/agent/tools/memory.py +48 -0
- janito/agent/tools/move_file.py +72 -23
- janito/agent/tools/outline_file/__init__.py +85 -0
- janito/agent/tools/outline_file/formatting.py +20 -0
- janito/agent/tools/outline_file/markdown_outline.py +14 -0
- janito/agent/tools/outline_file/python_outline.py +71 -0
- janito/agent/tools/present_choices.py +62 -0
- janito/agent/tools/present_choices_test.py +18 -0
- janito/agent/tools/remove_directory.py +31 -26
- janito/agent/tools/remove_file.py +31 -13
- janito/agent/tools/replace_text_in_file.py +135 -36
- janito/agent/tools/run_bash_command.py +113 -97
- janito/agent/tools/run_powershell_command.py +169 -0
- janito/agent/tools/run_python_command.py +53 -29
- janito/agent/tools/search_outline.py +17 -0
- janito/agent/tools/search_text.py +208 -0
- janito/agent/tools/tools_utils.py +47 -4
- janito/agent/tools/utils.py +14 -15
- janito/agent/tools/validate_file_syntax.py +163 -0
- janito/cli/_print_config.py +1 -1
- janito/cli/arg_parser.py +36 -4
- janito/cli/config_commands.py +1 -1
- janito/cli/logging_setup.py +7 -2
- janito/cli/main.py +97 -3
- janito/cli/runner/__init__.py +0 -2
- janito/cli/runner/_termweb_log_utils.py +17 -0
- janito/cli/runner/cli_main.py +121 -89
- janito/cli/runner/config.py +6 -4
- janito/cli/termweb_starter.py +73 -0
- janito/cli_chat_shell/chat_loop.py +52 -13
- janito/cli_chat_shell/chat_state.py +1 -1
- janito/cli_chat_shell/chat_ui.py +2 -3
- janito/cli_chat_shell/commands/__init__.py +17 -6
- janito/cli_chat_shell/commands/{history_reset.py → history_start.py} +13 -5
- janito/cli_chat_shell/commands/lang.py +16 -0
- janito/cli_chat_shell/commands/prompt.py +42 -0
- janito/cli_chat_shell/commands/session_control.py +36 -1
- janito/cli_chat_shell/commands/sum.py +49 -0
- janito/cli_chat_shell/commands/termweb_log.py +86 -0
- janito/cli_chat_shell/commands/utility.py +5 -2
- janito/cli_chat_shell/commands/verbose.py +29 -0
- janito/cli_chat_shell/load_prompt.py +47 -8
- janito/cli_chat_shell/session_manager.py +9 -1
- janito/cli_chat_shell/shell_command_completer.py +20 -0
- janito/cli_chat_shell/ui.py +110 -93
- janito/i18n/__init__.py +35 -0
- janito/i18n/messages.py +23 -0
- janito/i18n/pt.py +46 -0
- janito/rich_utils.py +43 -43
- janito/termweb/app.py +95 -0
- janito/termweb/static/editor.html +238 -0
- janito/termweb/static/editor.html.bak +238 -0
- janito/termweb/static/explorer.html.bak +59 -0
- janito/termweb/static/favicon.ico +0 -0
- janito/termweb/static/favicon.ico.bak +0 -0
- janito/termweb/static/index.html +55 -0
- janito/termweb/static/index.html.bak +55 -0
- janito/termweb/static/index.html.bak.bak +175 -0
- janito/termweb/static/landing.html.bak +36 -0
- janito/termweb/static/termicon.svg +1 -0
- janito/termweb/static/termweb.css +235 -0
- janito/termweb/static/termweb.css.bak +286 -0
- janito/termweb/static/termweb.js +187 -0
- janito/termweb/static/termweb.js.bak +187 -0
- janito/termweb/static/termweb.js.bak.bak +157 -0
- janito/termweb/static/termweb_quickopen.js +135 -0
- janito/termweb/static/termweb_quickopen.js.bak +125 -0
- janito/web/app.py +10 -13
- {janito-1.6.0.dist-info → janito-1.8.0.dist-info}/METADATA +73 -32
- janito-1.8.0.dist-info/RECORD +127 -0
- {janito-1.6.0.dist-info → janito-1.8.0.dist-info}/WHEEL +1 -1
- janito/agent/tool_registry_core.py +0 -2
- janito/agent/tools/get_file_outline.py +0 -117
- janito/agent/tools/py_compile_file.py +0 -40
- janito/agent/tools/replace_file.py +0 -51
- janito/agent/tools/search_files.py +0 -71
- janito/cli/runner/scan.py +0 -44
- janito/cli_chat_shell/commands/system.py +0 -73
- janito-1.6.0.dist-info/RECORD +0 -81
- {janito-1.6.0.dist-info → janito-1.8.0.dist-info}/entry_points.txt +0 -0
- {janito-1.6.0.dist-info → janito-1.8.0.dist-info}/licenses/LICENSE +0 -0
- {janito-1.6.0.dist-info → janito-1.8.0.dist-info}/top_level.txt +0 -0
janito/cli_chat_shell/ui.py
CHANGED
@@ -2,55 +2,29 @@ from prompt_toolkit import PromptSession
|
|
2
2
|
from prompt_toolkit.enums import EditingMode
|
3
3
|
from prompt_toolkit.formatted_text import HTML
|
4
4
|
from prompt_toolkit.styles import Style
|
5
|
+
from prompt_toolkit.key_binding import KeyBindings
|
5
6
|
from janito.agent.runtime_config import runtime_config
|
6
|
-
from .
|
7
|
+
from janito.i18n import tr
|
7
8
|
|
8
9
|
|
9
10
|
def print_summary(console, data, continue_session):
|
10
11
|
if not data:
|
11
12
|
return
|
12
|
-
msgs = data.get("messages", [])
|
13
|
-
last_user = next(
|
14
|
-
(m["content"] for m in reversed(msgs) if m.get("role") == "user"), None
|
15
|
-
)
|
16
|
-
last_assistant = next(
|
17
|
-
(m["content"] for m in reversed(msgs) if m.get("role") == "assistant"), None
|
18
|
-
)
|
19
|
-
usage = data.get("last_usage_info", {})
|
20
13
|
console.print("[bold cyan]Last saved conversation:[/bold cyan]")
|
21
|
-
console.print(f"Messages: {len(msgs)}")
|
22
|
-
if last_user:
|
23
|
-
console.print(
|
24
|
-
f"Last user: [italic]{last_user[:100]}{'...' if len(last_user)>100 else ''}[/italic]"
|
25
|
-
)
|
26
|
-
if last_assistant:
|
27
|
-
console.print(
|
28
|
-
f"Last assistant: [italic]{last_assistant[:100]}{'...' if len(last_assistant)>100 else ''}[/italic]"
|
29
|
-
)
|
30
|
-
if usage:
|
31
|
-
ptok = usage.get("prompt_tokens")
|
32
|
-
ctok = usage.get("completion_tokens")
|
33
|
-
tot = (ptok or 0) + (ctok or 0)
|
34
|
-
console.print(f"Tokens - Prompt: {ptok}, Completion: {ctok}, Total: {tot}")
|
35
|
-
# Only print /continue suggestion if a last conversation exists
|
36
|
-
if not continue_session and last_conversation_exists():
|
37
|
-
console.print(
|
38
|
-
"[bold yellow]Type /continue to restore the last saved conversation.[/bold yellow]"
|
39
|
-
)
|
40
14
|
|
41
15
|
|
42
16
|
def print_welcome(console, version=None, continued=False):
|
43
17
|
version_str = f" (v{version})" if version else ""
|
44
|
-
|
45
|
-
if vanilla_mode:
|
18
|
+
if runtime_config.get("vanilla_mode", False):
|
46
19
|
console.print(
|
47
|
-
f"[bold magenta]Welcome to Janito{version_str} in [white on magenta]VANILLA MODE[/white on magenta]! Tools, system prompt, and temperature are disabled unless overridden.[/bold magenta]\n
|
20
|
+
f"[bold magenta]{tr('Welcome to Janito{version_str} in [white on magenta]VANILLA MODE[/white on magenta]! Tools, system prompt, and temperature are disabled unless overridden.', version_str=version_str)}[/bold magenta]\n"
|
21
|
+
f"[cyan]{tr('F12 = Quick Action (follows the recommended action)')}[/cyan]"
|
48
22
|
)
|
49
23
|
else:
|
50
24
|
console.print(
|
51
|
-
f"[bold green]Welcome to Janito{version_str}! Entering chat mode. Type /exit to exit.[/bold green]\n
|
25
|
+
f"[bold green]{tr('Welcome to Janito{version_str}! Entering chat mode. Type /exit to exit.', version_str=version_str)}[/bold green]\n"
|
26
|
+
f"[cyan]{tr('F12 = Quick Action (follows the recommended action)')}[/cyan]"
|
52
27
|
)
|
53
|
-
# Only print /continue suggestion if a last conversation exists
|
54
28
|
|
55
29
|
|
56
30
|
def get_toolbar_func(
|
@@ -60,113 +34,156 @@ def get_toolbar_func(
|
|
60
34
|
model_name=None,
|
61
35
|
role_ref=None,
|
62
36
|
style_ref=None,
|
37
|
+
version=None,
|
63
38
|
):
|
64
|
-
|
39
|
+
from prompt_toolkit.application.current import get_app
|
40
|
+
|
41
|
+
def format_tokens(n, tag=None):
|
65
42
|
if n is None:
|
66
43
|
return "?"
|
67
|
-
if n
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
44
|
+
if n < 1000:
|
45
|
+
val = str(n)
|
46
|
+
elif n < 1000000:
|
47
|
+
val = f"{n/1000:.1f}k"
|
48
|
+
else:
|
49
|
+
val = f"{n/1000000:.1f}M"
|
50
|
+
return f"<{tag}>{val}</{tag}>" if tag else val
|
72
51
|
|
73
52
|
def get_toolbar():
|
74
|
-
left = f" Messages: <msg_count>{len(messages_ref())}</msg_count>"
|
75
|
-
usage = last_usage_info_ref()
|
76
|
-
last_elapsed = last_elapsed_ref()
|
77
|
-
if usage:
|
78
|
-
prompt_tokens = usage.get("prompt_tokens")
|
79
|
-
completion_tokens = usage.get("completion_tokens")
|
80
|
-
total_tokens = (prompt_tokens or 0) + (completion_tokens or 0)
|
81
|
-
speed = None
|
82
|
-
if last_elapsed and last_elapsed > 0:
|
83
|
-
speed = total_tokens / last_elapsed
|
84
|
-
left += (
|
85
|
-
f" | Tokens: In=<tokens_in>{format_tokens(prompt_tokens)}</tokens_in> / "
|
86
|
-
f"Out=<tokens_out>{format_tokens(completion_tokens)}</tokens_out> / "
|
87
|
-
f"Total=<tokens_total>{format_tokens(total_tokens)}</tokens_total>"
|
88
|
-
)
|
89
|
-
if speed is not None:
|
90
|
-
left += f", speed=<speed>{speed:.1f}</speed> tokens/sec"
|
91
|
-
|
92
|
-
from prompt_toolkit.application import get_app
|
93
|
-
|
94
53
|
width = get_app().output.get_size().columns
|
95
|
-
model_part =
|
54
|
+
model_part = (
|
55
|
+
f" {tr('Model')}: <model>{model_name}</model>" if model_name else ""
|
56
|
+
)
|
96
57
|
role_part = ""
|
97
58
|
vanilla_mode = runtime_config.get("vanilla_mode", False)
|
98
59
|
if role_ref and not vanilla_mode:
|
99
60
|
role = role_ref()
|
100
61
|
if role:
|
101
|
-
role_part = f"Role: <
|
62
|
+
role_part = f"{tr('Role')}: <role>{role}</role>"
|
63
|
+
|
102
64
|
style_part = ""
|
103
65
|
if style_ref:
|
104
66
|
style = style_ref()
|
105
67
|
if style:
|
106
|
-
style_part = f"Style: <b>{style}</b>"
|
68
|
+
style_part = f"{tr('Style')}: <b>{style}</b>"
|
69
|
+
usage = last_usage_info_ref()
|
70
|
+
prompt_tokens = usage.get("prompt_tokens") if usage else None
|
71
|
+
completion_tokens = usage.get("completion_tokens") if usage else None
|
72
|
+
total_tokens = usage.get("total_tokens") if usage else None
|
107
73
|
first_line_parts = []
|
108
74
|
if model_part:
|
109
75
|
first_line_parts.append(model_part)
|
76
|
+
|
110
77
|
if role_part:
|
111
78
|
first_line_parts.append(role_part)
|
112
79
|
if style_part:
|
113
80
|
first_line_parts.append(style_part)
|
114
81
|
first_line = " | ".join(first_line_parts)
|
115
|
-
|
116
|
-
|
82
|
+
left = f" {tr('Messages')}: <msg_count>{len(messages_ref())}</msg_count>"
|
83
|
+
tokens_part = ""
|
84
|
+
if (
|
85
|
+
prompt_tokens is not None
|
86
|
+
or completion_tokens is not None
|
87
|
+
or total_tokens is not None
|
88
|
+
):
|
89
|
+
tokens_part = (
|
90
|
+
f" | {tr('Tokens')} - {tr('Prompt')}: {format_tokens(prompt_tokens, 'tokens_in')}, "
|
91
|
+
f"{tr('Completion')}: {format_tokens(completion_tokens, 'tokens_out')}, "
|
92
|
+
f"{tr('Total')}: {format_tokens(total_tokens, 'tokens_total')}"
|
93
|
+
)
|
94
|
+
# Move /help and /start tips to key bindings/info line
|
95
|
+
# Compose second/status line (no help_part)
|
96
|
+
second_line = f"{left}{tokens_part}"
|
97
|
+
total_len = len(left) + len(tokens_part)
|
117
98
|
if first_line:
|
118
99
|
total_len += len(first_line) + 3
|
119
100
|
if total_len < width:
|
120
101
|
padding = " " * (width - total_len)
|
121
|
-
second_line = f"{left}{
|
122
|
-
|
123
|
-
|
102
|
+
second_line = f"{left}{tokens_part}{padding}"
|
103
|
+
# Add key bindings info as an extra line, now including /help and /start
|
104
|
+
bindings_line = (
|
105
|
+
f"<b> F12</b>: {tr('Quick Action')} | "
|
106
|
+
f"<b>Ctrl-Y</b>: {tr('Yes')} | "
|
107
|
+
f"<b>Ctrl-N</b>: {tr('No')} | "
|
108
|
+
f"<b>/help</b>: {tr('Help')} | "
|
109
|
+
f"<b>/start</b>: {tr('New Task')}"
|
110
|
+
)
|
124
111
|
if first_line:
|
125
|
-
toolbar_text = first_line + "\n" + second_line
|
112
|
+
toolbar_text = first_line + "\n" + second_line + "\n" + bindings_line
|
126
113
|
else:
|
127
|
-
toolbar_text = second_line
|
114
|
+
toolbar_text = second_line + "\n" + bindings_line
|
128
115
|
return HTML(toolbar_text)
|
129
116
|
|
130
117
|
return get_toolbar
|
131
118
|
|
132
119
|
|
133
|
-
def
|
134
|
-
|
120
|
+
def get_custom_key_bindings():
|
121
|
+
"""
|
122
|
+
Returns prompt_toolkit KeyBindings for custom CLI shortcuts:
|
123
|
+
- F12: Cycles through quick action phrases and submits.
|
124
|
+
- Ctrl-Y: Inserts 'Yes' and submits (for confirmation prompts).
|
125
|
+
- Ctrl-N: Inserts 'No' and submits (for confirmation prompts).
|
126
|
+
"""
|
127
|
+
bindings = KeyBindings()
|
128
|
+
_f12_instructions = ["proceed", "go ahead", "continue", "next", "okay"]
|
129
|
+
_f12_index = {"value": 0}
|
135
130
|
|
131
|
+
@bindings.add("f12")
|
132
|
+
def _(event):
|
133
|
+
buf = event.app.current_buffer
|
134
|
+
idx = _f12_index["value"]
|
135
|
+
buf.text = _f12_instructions[idx]
|
136
|
+
buf.validate_and_handle()
|
137
|
+
_f12_index["value"] = (idx + 1) % len(_f12_instructions)
|
138
|
+
|
139
|
+
@bindings.add("c-y")
|
140
|
+
def _(event):
|
141
|
+
buf = event.app.current_buffer
|
142
|
+
buf.text = "Yes"
|
143
|
+
buf.validate_and_handle()
|
144
|
+
|
145
|
+
@bindings.add("c-n")
|
146
|
+
def _(event):
|
147
|
+
buf = event.app.current_buffer
|
148
|
+
buf.text = "No"
|
149
|
+
buf.validate_and_handle()
|
150
|
+
|
151
|
+
return bindings
|
152
|
+
|
153
|
+
|
154
|
+
def get_prompt_session(get_toolbar_func, mem_history):
|
136
155
|
style = Style.from_dict(
|
137
156
|
{
|
138
157
|
"bottom-toolbar": "bg:#333333 #ffffff",
|
139
|
-
"
|
140
|
-
"
|
141
|
-
"model": "bold bg:#005f5f #ffffff", # distinct background/foreground
|
142
|
-
"msg_count": "bg:#333333 #ffff00 bold",
|
158
|
+
"model": "bold bg:#005f5f #ffffff",
|
159
|
+
"role": "bold ansiyellow",
|
143
160
|
"tokens_in": "ansicyan bold",
|
144
161
|
"tokens_out": "ansigreen bold",
|
145
162
|
"tokens_total": "ansiyellow bold",
|
146
|
-
"
|
147
|
-
"
|
148
|
-
"
|
149
|
-
|
163
|
+
"msg_count": "bg:#333333 #ffff00 bold",
|
164
|
+
"b": "bold",
|
165
|
+
"prompt": "bg:#005f5f #ffffff", # (legacy, not used)
|
166
|
+
# Style for prompt_toolkit input line:
|
167
|
+
# - '': affects the actual user input area background (full line, most reliable)
|
168
|
+
# - "input-field": also affects the input area in some prompt_toolkit versions
|
169
|
+
# - <inputline> tag: only affects the prompt label, not the input area
|
170
|
+
"": "bg:#005fdd #ffffff", # Blue background for the user input area (recommended)
|
171
|
+
"input-field": "bg:#005fdd #ffffff", # Blue background for the user input area (optional)
|
172
|
+
"inputline": "bg:#005fdd #ffffff", # Blue background for the prompt label (icon/text)
|
150
173
|
}
|
151
174
|
)
|
152
|
-
|
153
|
-
|
154
|
-
@kb.add("f12")
|
155
|
-
def _(event):
|
156
|
-
"""When F12 is pressed, send 'proceed' as input immediately."""
|
157
|
-
buf = event.app.current_buffer
|
158
|
-
buf.text = "proceed"
|
159
|
-
buf.validate_and_handle()
|
175
|
+
from .shell_command_completer import ShellCommandCompleter
|
160
176
|
|
161
|
-
|
162
|
-
|
163
|
-
key_bindings=kb,
|
164
|
-
editing_mode=EditingMode.EMACS,
|
177
|
+
completer = ShellCommandCompleter()
|
178
|
+
return PromptSession(
|
165
179
|
bottom_toolbar=get_toolbar_func,
|
166
180
|
style=style,
|
181
|
+
editing_mode=EditingMode.VI,
|
182
|
+
key_bindings=get_custom_key_bindings(),
|
167
183
|
history=mem_history,
|
184
|
+
completer=completer,
|
168
185
|
)
|
169
|
-
return session
|
170
186
|
|
171
187
|
|
172
|
-
|
188
|
+
def _(text):
|
189
|
+
return text
|
janito/i18n/__init__.py
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
import importlib
|
2
|
+
import threading
|
3
|
+
import hashlib
|
4
|
+
|
5
|
+
_current_locale = "en"
|
6
|
+
_translations = {}
|
7
|
+
_lock = threading.Lock()
|
8
|
+
|
9
|
+
|
10
|
+
def set_locale(locale):
|
11
|
+
global _current_locale, _translations
|
12
|
+
with _lock:
|
13
|
+
_current_locale = locale
|
14
|
+
if locale == "en":
|
15
|
+
_translations = {}
|
16
|
+
else:
|
17
|
+
try:
|
18
|
+
mod = importlib.import_module(f"janito.i18n.{locale}")
|
19
|
+
_translations = getattr(mod, "translations", {})
|
20
|
+
except ImportError:
|
21
|
+
_translations = {}
|
22
|
+
|
23
|
+
|
24
|
+
def tr(msg, **kwargs):
|
25
|
+
"""Translate message to current locale, usando hash SHA-1 da mensagem como chave."""
|
26
|
+
msg_hash = hashlib.sha1(msg.encode("utf-8")).hexdigest()
|
27
|
+
template = _translations.get(msg_hash, msg)
|
28
|
+
try:
|
29
|
+
return template.format(**kwargs)
|
30
|
+
except Exception:
|
31
|
+
return template # fallback if formatting fails
|
32
|
+
|
33
|
+
|
34
|
+
# Inicializa com o idioma padrão (en)
|
35
|
+
set_locale("en")
|
janito/i18n/messages.py
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# Arquivo base de mensagens para i18n
|
2
|
+
# Chave = SHA-1 da string original em inglês
|
3
|
+
|
4
|
+
MESSAGES = {
|
5
|
+
# create_directory.py
|
6
|
+
"e3d8e1e5e0b3e4a2a7a5e2e1e4e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0": "Successfully created the directory at '{path}'.",
|
7
|
+
"f2b7e4e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1": "Cannot create directory: '{path}' already exists.",
|
8
|
+
"d3e2e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5": "Path '{path}' exists and is not a directory.",
|
9
|
+
"b1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1": "Error creating directory '{path}': {error}",
|
10
|
+
# create_file.py
|
11
|
+
"a2e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0": "File already exists at '{path}'. Use overwrite=True to overwrite.",
|
12
|
+
"c3e2e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5": "Created file ({lines} lines).",
|
13
|
+
"d4e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0": "Updated file ({lines} lines, backup at {backup_path}).",
|
14
|
+
# remove_file.py
|
15
|
+
"e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5": "File '{path}' does not exist.",
|
16
|
+
"f6e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0": "Path '{path}' is not a file.",
|
17
|
+
"a7e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1": "Successfully removed the file at '{path}'.",
|
18
|
+
"b8e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0": "Error removing file: {error}",
|
19
|
+
# replace_text_in_file.py (exemplo)
|
20
|
+
"c9e2e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5": "Text replaced in {file_path} (backup at {backup_path}). {details}",
|
21
|
+
"d0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0": "No changes made. {warning_detail}",
|
22
|
+
"e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1e5e0e1": "Error replacing text: {error}",
|
23
|
+
}
|
janito/i18n/pt.py
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
translations = {
|
2
|
+
"36107ed78ab25f6fb12ad8ce13018cd1ce6735d1": "Iniciando servidor web...",
|
3
|
+
"70a0d194687568a47aa617fd85036ace1e69a982": "Deseja realmente sair? (s/n): ",
|
4
|
+
"5c9ebcbbd7632ecb328bd52958b17158afaa32c6": "F12 = Ação Rápida (segue a ação recomendada)",
|
5
|
+
"e4034394acca752c021b2ab50f60da8273e3c314": "TermWeb iniciado... Disponível em http://localhost:{selected_port}",
|
6
|
+
"fe21121e2934234b68d19b2757532117d440c1e3": "Chave de API não encontrada. Por favor, configure 'api_key' no seu arquivo de configuração.",
|
7
|
+
"c9e3759b1756eba35b381ce2b72cd659e132b01f": "Olá, {name}!",
|
8
|
+
"ca1fee2f55baabdc2e4b0e9529c89ee024e62079": "Nenhum prompt fornecido nas mensagens",
|
9
|
+
"f7449d23d0c500ae2a0b31e04f92b47a4d8ae845": "max_tokens deve ser um inteiro, recebido: {resolved_max_tokens!r}",
|
10
|
+
"70a9ed8edb6da12e208431a31aa16ba54419b26f": "Resposta inválida/malformada do OpenAI (tentativa {attempt}/{max_retries}). Tentando novamente em {wait_time} segundos...",
|
11
|
+
"a873085e3b06184fb5d27e842f97b06b6190976d": "Número máximo de tentativas para resposta inválida atingido. Gerando erro.",
|
12
|
+
"66a34568bbe846bb1bde3619eb4d6dfa10211104": "A API não suporta o uso de ferramentas.",
|
13
|
+
"09b81476b75586da4116b83f8be70d77b174cec3": "Limit de taxa da API OpenAI (429) (tentativa {attempt}/{max_retries}): {e}. Tentando novamente em {wait_time} segundos...",
|
14
|
+
"5717a35dd2a1533fb7e15edc8c9329cb69f3410b": "Erro do servidor da API OpenAI (tentativa {attempt}/{max_retries}): {e}. Tentando novamente em {wait_time} segundos...",
|
15
|
+
"02e760ba15ed863176c1290ac8a9b923963103cd": "Erro do client da API OpenAI {status_code}: {e}. Não será feita nova tentativa.",
|
16
|
+
"2e52b0bbc8f16226b70e3e20f95c9245d2bcdb47": "Erro da API OpenAI (tentativa {attempt}/{max_retries}): {e}. Tentando novamente em {wait_time} segundos...",
|
17
|
+
"012cc970e039fdd79c452fc676202c814ffc76ae": "Número máximo de tentativas para erro da API OpenAI atingido. Gerando erro.",
|
18
|
+
"d0438e45667d31e0022b2497b5901cd4300f084b": "QueuedMessageHandler.handle_message espera um dicionário com 'type' e 'message', recebido {msg_type}: {msg!r}",
|
19
|
+
"9d3460187ffa19c7c8a4020157072b1087e1bd2f": "[QueuedMessageHandler] {msg_type}: {msg}",
|
20
|
+
"3813833343430e8afa8fce33385c5e39fb24dd60": "[QueuedMessageHandler] {msg_type}: {message}",
|
21
|
+
"0be9a22226e16a40797010d23a0f581542dca40e": "[ToolExecutor] {tool_name} chamado com arguments: {args}",
|
22
|
+
"42c68edcb25442f518b1af77c6a2ddc07461aae0": "[ToolExecutor] Motivo da chamada: {tool_call_reason}",
|
23
|
+
"002ff598115d84595ffeee6219cb5c03d3a1d4a6": "Pergunta",
|
24
|
+
"35747d13dcd91e8e8790c7f767d5ed764f541b9e": "prosseguir",
|
25
|
+
"33dde3a1afbc418768a69fa53168d9b0638fe1aa": "avançar",
|
26
|
+
"eee0bbba4ff92adbeb038a77df0466d660f15716": "continuar",
|
27
|
+
"edee9402d198b04ac77dcf5dc9cc3dac44573782": "próximo",
|
28
|
+
"8fdb7e2fa84f4faf0d9b92f466df424ec47a165b": "ok",
|
29
|
+
"5f8f924671cda79b5205a6bf1b776f347c4a7a07": "Opções de configuração disponíveis:\n",
|
30
|
+
"cef780a309cd234750764d42697882c24168ddab": "{key:15} {desc} (padrão: {default})",
|
31
|
+
"68c2cc7f0ceaa3e499ecb4db331feb4debbbcc23": "Modelo",
|
32
|
+
"c3f104d1365744b538bfde9f4adb6a6df4b80355": "Função",
|
33
|
+
"99a0efc6cfd85d8ff2732a6718140f822cb90472": "Estilo",
|
34
|
+
"f1702b4686278becffc88baabe6f4b7a8355532c": "Mensagens",
|
35
|
+
"c38c6c1f3a2743f8626703abb302e403d20ff81c": "Tokens",
|
36
|
+
"a817d7eb8e0f1dab755ab5203a082e5c3c094fce": "Prompt",
|
37
|
+
"2ff255684a2277f806fcebf3fe338ed27857f350": "Conclusão",
|
38
|
+
"b25928c69902557b0ef0a628490a3a1768d7b82f": "Total",
|
39
|
+
"76e63d65c883ed50df40ac3aeef0c2d6a1c4ad60": "Ação Rápida",
|
40
|
+
"5397e0583f14f6c88de06b1ef28f460a1fb5b0ae": "Sim",
|
41
|
+
"816c52fd2bdd94a63cd0944823a6c0aa9384c103": "Não",
|
42
|
+
"c47ae15370cfe1ed2781eedc1dc2547d12d9e972": "Ajuda",
|
43
|
+
"cc3dbd47e1cf9003a55d3366b3adbcd72275e525": "Nova Tarefa",
|
44
|
+
"efa5a8b84e1afe65c81ecfce28c398c48f19ddc2": "Bem-vindo ao Janito{version_str}! Entrando no modo de chat. Digite /exit para sair.",
|
45
|
+
"b314d6e1460f86e0f21abc5aceb7935a2a0667e8": "Bem-vindo ao Janito{version_str} no [white on magenta]MODO VANILLA[/white on magenta]! Ferramentas, prompt do sistema e temperatura estão desativados, a menos que sejam substituídos.",
|
46
|
+
}
|
janito/rich_utils.py
CHANGED
@@ -1,43 +1,43 @@
|
|
1
|
-
"""
|
2
|
-
Utilities for working with the Rich library.
|
3
|
-
"""
|
4
|
-
|
5
|
-
from rich.markdown import Markdown
|
6
|
-
from rich.text import Text
|
7
|
-
from rich.console import Console
|
8
|
-
|
9
|
-
|
10
|
-
def print_markdown(console: Console, message: str):
|
11
|
-
console.print(Markdown(message))
|
12
|
-
|
13
|
-
|
14
|
-
def print_info(console: Console, message: str):
|
15
|
-
console.print(message, style="cyan", end="")
|
16
|
-
|
17
|
-
|
18
|
-
def print_success(console: Console, message: str):
|
19
|
-
console.print(message, style="bold green", end="\n")
|
20
|
-
|
21
|
-
|
22
|
-
def print_error(console: Console, message: str):
|
23
|
-
console.print(message, style="bold red", end="\n")
|
24
|
-
|
25
|
-
|
26
|
-
def print_warning(console: Console, message: str):
|
27
|
-
console.print(message, style="bold yellow", end="\n")
|
28
|
-
|
29
|
-
|
30
|
-
def print_magenta(console: Console, message: str):
|
31
|
-
console.print(message, style="magenta", end="\n")
|
32
|
-
|
33
|
-
|
34
|
-
def print_stdout(console: Console, message: str):
|
35
|
-
console.print(
|
36
|
-
Text(message, style="on #003300", no_wrap=True, overflow=None), end=""
|
37
|
-
)
|
38
|
-
|
39
|
-
|
40
|
-
def print_stderr(console: Console, message: str):
|
41
|
-
console.print(
|
42
|
-
Text(message, style="on #330000", no_wrap=True, overflow=None), end=""
|
43
|
-
)
|
1
|
+
"""
|
2
|
+
Utilities for working with the Rich library.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from rich.markdown import Markdown
|
6
|
+
from rich.text import Text
|
7
|
+
from rich.console import Console
|
8
|
+
|
9
|
+
|
10
|
+
def print_markdown(console: Console, message: str):
|
11
|
+
console.print(Markdown(message))
|
12
|
+
|
13
|
+
|
14
|
+
def print_info(console: Console, message: str):
|
15
|
+
console.print(message, style="cyan", end="")
|
16
|
+
|
17
|
+
|
18
|
+
def print_success(console: Console, message: str):
|
19
|
+
console.print(message, style="bold green", end="\n")
|
20
|
+
|
21
|
+
|
22
|
+
def print_error(console: Console, message: str):
|
23
|
+
console.print(message, style="bold red", end="\n")
|
24
|
+
|
25
|
+
|
26
|
+
def print_warning(console: Console, message: str):
|
27
|
+
console.print(message, style="bold yellow", end="\n")
|
28
|
+
|
29
|
+
|
30
|
+
def print_magenta(console: Console, message: str):
|
31
|
+
console.print(message, style="magenta", end="\n")
|
32
|
+
|
33
|
+
|
34
|
+
def print_stdout(console: Console, message: str):
|
35
|
+
console.print(
|
36
|
+
Text(message, style="on #003300", no_wrap=True, overflow=None), end=""
|
37
|
+
)
|
38
|
+
|
39
|
+
|
40
|
+
def print_stderr(console: Console, message: str):
|
41
|
+
console.print(
|
42
|
+
Text(message, style="on #330000", no_wrap=True, overflow=None), end=""
|
43
|
+
)
|
janito/termweb/app.py
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
from quart import Quart, send_from_directory, request, jsonify, websocket
|
2
|
+
import os
|
3
|
+
from janito.agent.tools.dir_walk_utils import walk_dir_with_gitignore
|
4
|
+
|
5
|
+
app = Quart(__name__)
|
6
|
+
|
7
|
+
BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))
|
8
|
+
|
9
|
+
|
10
|
+
@app.route("/")
|
11
|
+
async def index():
|
12
|
+
static_dir = os.path.join(os.path.dirname(__file__), "static")
|
13
|
+
file_path = request.args.get("path")
|
14
|
+
if file_path:
|
15
|
+
return await send_from_directory(static_dir, "editor.html")
|
16
|
+
return await send_from_directory(static_dir, "index.html")
|
17
|
+
|
18
|
+
|
19
|
+
@app.route("/static/<path:filepath>")
|
20
|
+
async def server_static(filepath):
|
21
|
+
static_dir = os.path.join(os.path.dirname(__file__), "static")
|
22
|
+
return await send_from_directory(static_dir, filepath)
|
23
|
+
|
24
|
+
|
25
|
+
@app.route("/api/explorer/")
|
26
|
+
async def api_explorer_root():
|
27
|
+
return await api_explorer(".")
|
28
|
+
|
29
|
+
|
30
|
+
@app.route("/api/explorer/<path:path>", methods=["GET", "POST"])
|
31
|
+
async def api_explorer(path="."):
|
32
|
+
abs_path = os.path.abspath(os.path.join(BASE_DIR, path))
|
33
|
+
# Security: Only allow files/dirs within BASE_DIR
|
34
|
+
if not abs_path.startswith(BASE_DIR):
|
35
|
+
return jsonify({"error": "Acesso negado."}), 403
|
36
|
+
|
37
|
+
if request.method == "POST":
|
38
|
+
# Gravação de arquivo
|
39
|
+
if os.path.isdir(abs_path):
|
40
|
+
return jsonify({"error": "Não é possível gravar em um diretório."}), 400
|
41
|
+
try:
|
42
|
+
data = await request.get_json()
|
43
|
+
content = data.get("content", "")
|
44
|
+
with open(abs_path, "w", encoding="utf-8") as f:
|
45
|
+
f.write(content)
|
46
|
+
return jsonify({"success": True})
|
47
|
+
except Exception as e:
|
48
|
+
return jsonify({"error": str(e)}), 500
|
49
|
+
|
50
|
+
# GET: comportamento actual
|
51
|
+
if os.path.isdir(abs_path):
|
52
|
+
entries = []
|
53
|
+
walker = walk_dir_with_gitignore(abs_path, recursive=False)
|
54
|
+
for root, dirs, files in walker:
|
55
|
+
for entry in sorted(dirs):
|
56
|
+
entries.append({"name": entry, "is_dir": True})
|
57
|
+
for entry in sorted(files):
|
58
|
+
entries.append({"name": entry, "is_dir": False})
|
59
|
+
return jsonify({"type": "dir", "path": path, "entries": entries})
|
60
|
+
elif os.path.isfile(abs_path):
|
61
|
+
with open(abs_path, "r", encoding="utf-8", errors="replace") as f:
|
62
|
+
content = f.read()
|
63
|
+
return jsonify({"type": "file", "path": path, "content": content})
|
64
|
+
else:
|
65
|
+
return jsonify({"error": "Não encontrado."}), 404
|
66
|
+
|
67
|
+
|
68
|
+
# Example WebSocket endpoint
|
69
|
+
@app.websocket("/ws")
|
70
|
+
async def ws():
|
71
|
+
while True:
|
72
|
+
data = await websocket.receive()
|
73
|
+
await websocket.send(f"Echo: {data}")
|
74
|
+
|
75
|
+
|
76
|
+
# Catch-all route for SPA navigation (excluding /static and /api)
|
77
|
+
@app.route("/<path:catchall>")
|
78
|
+
async def spa_catch_all(catchall):
|
79
|
+
static_dir = os.path.join(os.path.dirname(__file__), "static")
|
80
|
+
return await send_from_directory(static_dir, "index.html")
|
81
|
+
|
82
|
+
|
83
|
+
if __name__ == "__main__":
|
84
|
+
import sys
|
85
|
+
|
86
|
+
port = 8088
|
87
|
+
if "--port" in sys.argv:
|
88
|
+
idx = sys.argv.index("--port")
|
89
|
+
if idx + 1 < len(sys.argv):
|
90
|
+
try:
|
91
|
+
port = int(sys.argv[idx + 1])
|
92
|
+
except ValueError:
|
93
|
+
pass
|
94
|
+
print(f"Iniciando servidor web Quart em http://localhost:{port}")
|
95
|
+
app.run(host="localhost", port=port, debug=True)
|