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.
Files changed (117) hide show
  1. janito/__init__.py +1 -1
  2. janito/agent/config.py +3 -3
  3. janito/agent/config_defaults.py +3 -2
  4. janito/agent/conversation.py +73 -27
  5. janito/agent/conversation_api.py +104 -4
  6. janito/agent/conversation_exceptions.py +6 -0
  7. janito/agent/conversation_tool_calls.py +17 -3
  8. janito/agent/event.py +24 -0
  9. janito/agent/event_dispatcher.py +24 -0
  10. janito/agent/event_handler_protocol.py +5 -0
  11. janito/agent/event_system.py +15 -0
  12. janito/agent/message_handler.py +4 -1
  13. janito/agent/message_handler_protocol.py +5 -0
  14. janito/agent/openai_client.py +5 -6
  15. janito/agent/openai_schema_generator.py +23 -4
  16. janito/agent/platform_discovery.py +90 -0
  17. janito/agent/profile_manager.py +34 -110
  18. janito/agent/queued_message_handler.py +22 -3
  19. janito/agent/rich_message_handler.py +3 -1
  20. janito/agent/templates/profiles/system_prompt_template_base.txt.j2 +14 -0
  21. janito/agent/templates/profiles/system_prompt_template_base_pt.txt.j2 +13 -0
  22. janito/agent/test_handler_protocols.py +47 -0
  23. janito/agent/tests/__init__.py +1 -0
  24. janito/agent/tool_base.py +1 -1
  25. janito/agent/tool_executor.py +109 -0
  26. janito/agent/tool_registry.py +3 -75
  27. janito/agent/tool_use_tracker.py +46 -0
  28. janito/agent/tools/__init__.py +11 -8
  29. janito/agent/tools/ask_user.py +26 -12
  30. janito/agent/tools/create_directory.py +50 -18
  31. janito/agent/tools/create_file.py +60 -29
  32. janito/agent/tools/dir_walk_utils.py +16 -0
  33. janito/agent/tools/fetch_url.py +10 -11
  34. janito/agent/tools/find_files.py +49 -40
  35. janito/agent/tools/get_lines.py +60 -25
  36. janito/agent/tools/memory.py +48 -0
  37. janito/agent/tools/move_file.py +72 -23
  38. janito/agent/tools/outline_file/__init__.py +85 -0
  39. janito/agent/tools/outline_file/formatting.py +20 -0
  40. janito/agent/tools/outline_file/markdown_outline.py +14 -0
  41. janito/agent/tools/outline_file/python_outline.py +71 -0
  42. janito/agent/tools/present_choices.py +62 -0
  43. janito/agent/tools/present_choices_test.py +18 -0
  44. janito/agent/tools/remove_directory.py +31 -26
  45. janito/agent/tools/remove_file.py +31 -13
  46. janito/agent/tools/replace_text_in_file.py +135 -36
  47. janito/agent/tools/run_bash_command.py +113 -97
  48. janito/agent/tools/run_powershell_command.py +169 -0
  49. janito/agent/tools/run_python_command.py +53 -29
  50. janito/agent/tools/search_outline.py +17 -0
  51. janito/agent/tools/search_text.py +208 -0
  52. janito/agent/tools/tools_utils.py +47 -4
  53. janito/agent/tools/utils.py +14 -15
  54. janito/agent/tools/validate_file_syntax.py +163 -0
  55. janito/cli/_print_config.py +1 -1
  56. janito/cli/arg_parser.py +36 -4
  57. janito/cli/config_commands.py +1 -1
  58. janito/cli/logging_setup.py +7 -2
  59. janito/cli/main.py +97 -3
  60. janito/cli/runner/__init__.py +0 -2
  61. janito/cli/runner/_termweb_log_utils.py +17 -0
  62. janito/cli/runner/cli_main.py +121 -89
  63. janito/cli/runner/config.py +6 -4
  64. janito/cli/termweb_starter.py +73 -0
  65. janito/cli_chat_shell/chat_loop.py +52 -13
  66. janito/cli_chat_shell/chat_state.py +1 -1
  67. janito/cli_chat_shell/chat_ui.py +2 -3
  68. janito/cli_chat_shell/commands/__init__.py +17 -6
  69. janito/cli_chat_shell/commands/{history_reset.py → history_start.py} +13 -5
  70. janito/cli_chat_shell/commands/lang.py +16 -0
  71. janito/cli_chat_shell/commands/prompt.py +42 -0
  72. janito/cli_chat_shell/commands/session_control.py +36 -1
  73. janito/cli_chat_shell/commands/sum.py +49 -0
  74. janito/cli_chat_shell/commands/termweb_log.py +86 -0
  75. janito/cli_chat_shell/commands/utility.py +5 -2
  76. janito/cli_chat_shell/commands/verbose.py +29 -0
  77. janito/cli_chat_shell/load_prompt.py +47 -8
  78. janito/cli_chat_shell/session_manager.py +9 -1
  79. janito/cli_chat_shell/shell_command_completer.py +20 -0
  80. janito/cli_chat_shell/ui.py +110 -93
  81. janito/i18n/__init__.py +35 -0
  82. janito/i18n/messages.py +23 -0
  83. janito/i18n/pt.py +46 -0
  84. janito/rich_utils.py +43 -43
  85. janito/termweb/app.py +95 -0
  86. janito/termweb/static/editor.html +238 -0
  87. janito/termweb/static/editor.html.bak +238 -0
  88. janito/termweb/static/explorer.html.bak +59 -0
  89. janito/termweb/static/favicon.ico +0 -0
  90. janito/termweb/static/favicon.ico.bak +0 -0
  91. janito/termweb/static/index.html +55 -0
  92. janito/termweb/static/index.html.bak +55 -0
  93. janito/termweb/static/index.html.bak.bak +175 -0
  94. janito/termweb/static/landing.html.bak +36 -0
  95. janito/termweb/static/termicon.svg +1 -0
  96. janito/termweb/static/termweb.css +235 -0
  97. janito/termweb/static/termweb.css.bak +286 -0
  98. janito/termweb/static/termweb.js +187 -0
  99. janito/termweb/static/termweb.js.bak +187 -0
  100. janito/termweb/static/termweb.js.bak.bak +157 -0
  101. janito/termweb/static/termweb_quickopen.js +135 -0
  102. janito/termweb/static/termweb_quickopen.js.bak +125 -0
  103. janito/web/app.py +10 -13
  104. {janito-1.6.0.dist-info → janito-1.8.0.dist-info}/METADATA +73 -32
  105. janito-1.8.0.dist-info/RECORD +127 -0
  106. {janito-1.6.0.dist-info → janito-1.8.0.dist-info}/WHEEL +1 -1
  107. janito/agent/tool_registry_core.py +0 -2
  108. janito/agent/tools/get_file_outline.py +0 -117
  109. janito/agent/tools/py_compile_file.py +0 -40
  110. janito/agent/tools/replace_file.py +0 -51
  111. janito/agent/tools/search_files.py +0 -71
  112. janito/cli/runner/scan.py +0 -44
  113. janito/cli_chat_shell/commands/system.py +0 -73
  114. janito-1.6.0.dist-info/RECORD +0 -81
  115. {janito-1.6.0.dist-info → janito-1.8.0.dist-info}/entry_points.txt +0 -0
  116. {janito-1.6.0.dist-info → janito-1.8.0.dist-info}/licenses/LICENSE +0 -0
  117. {janito-1.6.0.dist-info → janito-1.8.0.dist-info}/top_level.txt +0 -0
@@ -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 .session_manager import last_conversation_exists
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
- vanilla_mode = runtime_config.get("vanilla_mode", False)
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[cyan]Quick action: Press F12 to continue. Double-check the suggested action first. 😊[/cyan]"
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[cyan]Quick action: Press F12 to continue. Double-check the suggested action first. 😊[/cyan]"
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
- def format_tokens(n):
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 >= 1_000_000:
68
- return f"{n/1_000_000:.1f}m"
69
- if n >= 1_000:
70
- return f"{n/1_000:.1f}k"
71
- return str(n)
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 = f" Model: <model>{model_name}</model>" if model_name else ""
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: <b>{role}</b>"
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
- help_part = "<b>/help</b> for help | <b>F12</b>: proceed"
116
- total_len = len(left) + len(help_part) + 3 # separators and spaces
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}{padding} | {help_part}"
122
- else:
123
- second_line = f"{left} | {help_part}"
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 get_prompt_session(get_toolbar_func, mem_history):
134
- from prompt_toolkit.key_binding import KeyBindings
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
- "b": "bold",
140
- "prompt": "bold bg:#000080 #ffffff",
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
- "speed": "ansimagenta bold",
147
- "right": "bg:#005f5f #ffffff",
148
- "input": "bg:#000080 #ffffff",
149
- "": "bg:#000080 #ffffff",
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
- kb = KeyBindings()
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
- session = PromptSession(
162
- multiline=False,
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
- # ... rest of the file remains unchanged ...
188
+ def _(text):
189
+ return text
@@ -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")
@@ -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)