janito 2.27.0__py3-none-any.whl → 2.28.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/README.md +9 -9
- janito/agent/setup_agent.py +29 -16
- janito/cli/chat_mode/script_runner.py +1 -1
- janito/cli/chat_mode/session.py +50 -24
- janito/cli/chat_mode/session_profile_select.py +8 -2
- janito/cli/chat_mode/shell/commands/__init__.py +2 -0
- janito/cli/chat_mode/shell/commands/execute.py +4 -2
- janito/cli/chat_mode/shell/commands/help.py +8 -1
- janito/cli/chat_mode/shell/commands/privileges.py +6 -2
- janito/cli/chat_mode/shell/commands/provider.py +28 -0
- janito/cli/chat_mode/shell/commands/read.py +4 -2
- janito/cli/chat_mode/shell/commands/security/__init__.py +1 -1
- janito/cli/chat_mode/shell/commands/security/allowed_sites.py +16 -13
- janito/cli/chat_mode/shell/commands/security_command.py +14 -10
- janito/cli/chat_mode/shell/commands/tools.py +4 -2
- janito/cli/chat_mode/shell/commands/unrestricted.py +17 -12
- janito/cli/chat_mode/shell/commands/write.py +4 -2
- janito/cli/chat_mode/toolbar.py +15 -1
- janito/cli/cli_commands/enable_disable_plugin.py +87 -0
- janito/cli/cli_commands/list_models.py +2 -2
- janito/cli/cli_commands/list_plugins.py +35 -19
- janito/cli/cli_commands/list_profiles.py +6 -6
- janito/cli/cli_commands/list_providers.py +1 -1
- janito/cli/cli_commands/model_utils.py +45 -20
- janito/cli/cli_commands/ping_providers.py +10 -10
- janito/cli/cli_commands/set_api_key.py +5 -3
- janito/cli/cli_commands/show_config.py +13 -7
- janito/cli/cli_commands/show_system_prompt.py +13 -6
- janito/cli/core/getters.py +7 -0
- janito/cli/core/model_guesser.py +18 -15
- janito/cli/core/runner.py +28 -6
- janito/cli/core/setters.py +21 -6
- janito/cli/main_cli.py +14 -12
- janito/cli/prompt_core.py +2 -0
- janito/cli/prompt_setup.py +4 -4
- janito/cli/single_shot_mode/handler.py +2 -0
- janito/config_manager.py +2 -0
- janito/docs/GETTING_STARTED.md +9 -9
- janito/drivers/cerebras/__init__.py +1 -1
- janito/exceptions.py +6 -4
- janito/plugins/__init__.py +2 -2
- janito/plugins/base.py +48 -40
- janito/plugins/builtin.py +88 -0
- janito/plugins/config.py +16 -19
- janito/plugins/discovery.py +129 -40
- janito/plugins/manager.py +63 -59
- janito/provider_registry.py +10 -10
- janito/providers/__init__.py +1 -1
- janito/providers/alibaba/model_info.py +3 -5
- janito/providers/alibaba/provider.py +3 -1
- janito/providers/cerebras/__init__.py +1 -1
- janito/providers/cerebras/model_info.py +12 -27
- janito/providers/cerebras/provider.py +11 -9
- janito/providers/mistral/__init__.py +1 -1
- janito/providers/mistral/model_info.py +1 -1
- janito/providers/mistral/provider.py +1 -1
- janito/providers/moonshot/__init__.py +1 -0
- janito/providers/{moonshotai → moonshot}/model_info.py +3 -3
- janito/providers/{moonshotai → moonshot}/provider.py +8 -8
- janito/providers/openai/provider.py +3 -1
- janito/report_events.py +0 -1
- janito/tools/adapters/local/ask_user.py +7 -1
- janito/tools/adapters/local/create_file.py +1 -1
- janito/tools/adapters/local/fetch_url.py +45 -29
- janito/tools/adapters/local/python_command_run.py +2 -1
- janito/tools/adapters/local/python_file_run.py +1 -0
- janito/tools/adapters/local/run_powershell_command.py +1 -1
- janito/tools/adapters/local/search_text/core.py +1 -1
- janito/tools/adapters/local/validate_file_syntax/jinja2_validator.py +14 -11
- janito/tools/base.py +12 -0
- janito/tools/loop_protection.py +24 -22
- janito/tools/path_utils.py +7 -7
- janito/tools/tool_base.py +0 -2
- janito/tools/tools_adapter.py +15 -5
- janito/tools/url_whitelist.py +27 -26
- {janito-2.27.0.dist-info → janito-2.28.0.dist-info}/METADATA +3 -1
- {janito-2.27.0.dist-info → janito-2.28.0.dist-info}/RECORD +81 -82
- janito-2.28.0.dist-info/top_level.txt +1 -0
- janito/providers/moonshotai/__init__.py +0 -1
- janito-2.27.0.dist-info/top_level.txt +0 -2
- janito-coder/janito_coder/__init__.py +0 -9
- janito-coder/janito_coder/plugins/__init__.py +0 -27
- janito-coder/janito_coder/plugins/code_navigator.py +0 -618
- janito-coder/janito_coder/plugins/git_analyzer.py +0 -273
- janito-coder/pyproject.toml +0 -347
- {janito-2.27.0.dist-info → janito-2.28.0.dist-info}/WHEEL +0 -0
- {janito-2.27.0.dist-info → janito-2.28.0.dist-info}/entry_points.txt +0 -0
- {janito-2.27.0.dist-info → janito-2.28.0.dist-info}/licenses/LICENSE +0 -0
janito/README.md
CHANGED
@@ -15,17 +15,17 @@ pip install janito
|
|
15
15
|
1. **Get your API key**: Sign up at [Moonshot AI](https://platform.moonshot.cn/) and get your API key
|
16
16
|
2. **Set your API key**:
|
17
17
|
```bash
|
18
|
-
janito --set-api-key YOUR_MOONSHOT_API_KEY -p
|
18
|
+
janito --set-api-key YOUR_MOONSHOT_API_KEY -p moonshot
|
19
19
|
```
|
20
20
|
|
21
21
|
### Basic Usage
|
22
22
|
|
23
|
-
**
|
23
|
+
**Moonshot (Recommended - Default Provider)**
|
24
24
|
```bash
|
25
|
-
# Using the default provider (
|
25
|
+
# Using the default provider (moonshot) and model
|
26
26
|
janito "Create a Python script that reads a CSV file"
|
27
27
|
|
28
|
-
# Using a specific
|
28
|
+
# Using a specific Moonshot model
|
29
29
|
janito -m kimi-k1-8k "Explain quantum computing"
|
30
30
|
```
|
31
31
|
|
@@ -71,13 +71,13 @@ In chat mode, you can:
|
|
71
71
|
|
72
72
|
Set default provider and model:
|
73
73
|
```bash
|
74
|
-
janito --set provider=
|
74
|
+
janito --set provider=moonshot
|
75
75
|
janito --set model=kimi-k1-8k
|
76
76
|
```
|
77
77
|
|
78
78
|
## Providers
|
79
79
|
|
80
|
-
###
|
80
|
+
### Moonshot (Recommended)
|
81
81
|
|
82
82
|
- **Models**: kimi-k1-8k, kimi-k1-32k, kimi-k1-128k, kimi-k2-turbo-preview
|
83
83
|
- **Strengths**: Excellent Chinese/English support, competitive pricing, fast responses
|
@@ -126,10 +126,10 @@ janito --role python-expert "Optimize this algorithm"
|
|
126
126
|
### Environment Variables
|
127
127
|
You can also configure via environment variables:
|
128
128
|
|
129
|
-
**
|
129
|
+
**Moonshot:**
|
130
130
|
```bash
|
131
|
-
export
|
132
|
-
export JANITO_PROVIDER=
|
131
|
+
export MOONSHOT_API_KEY=your_key_here
|
132
|
+
export JANITO_PROVIDER=moonshot
|
133
133
|
export JANITO_MODEL=kimi-k1-8k
|
134
134
|
```
|
135
135
|
|
janito/agent/setup_agent.py
CHANGED
@@ -53,38 +53,48 @@ def _load_template_content(profile, templates_dir):
|
|
53
53
|
return file.read(), user_template_path
|
54
54
|
|
55
55
|
# If nothing matched, list available profiles and raise an informative error
|
56
|
-
from janito.cli.cli_commands.list_profiles import
|
57
|
-
|
56
|
+
from janito.cli.cli_commands.list_profiles import (
|
57
|
+
_gather_default_profiles,
|
58
|
+
_gather_user_profiles,
|
59
|
+
)
|
60
|
+
|
58
61
|
default_profiles = _gather_default_profiles()
|
59
62
|
user_profiles = _gather_user_profiles()
|
60
|
-
|
63
|
+
|
61
64
|
available_profiles = []
|
62
65
|
if default_profiles:
|
63
66
|
available_profiles.extend([(p, "default") for p in default_profiles])
|
64
67
|
if user_profiles:
|
65
68
|
available_profiles.extend([(p, "user") for p in user_profiles])
|
66
|
-
|
69
|
+
|
67
70
|
# Normalize the input profile for better matching suggestions
|
68
71
|
normalized_input = re.sub(r"\s+", " ", profile.strip().lower())
|
69
|
-
|
72
|
+
|
70
73
|
if available_profiles:
|
71
|
-
profile_list = "\n".join(
|
72
|
-
|
74
|
+
profile_list = "\n".join(
|
75
|
+
[f" - {name} ({source})" for name, source in available_profiles]
|
76
|
+
)
|
77
|
+
|
73
78
|
# Find close matches
|
74
79
|
close_matches = []
|
75
80
|
for name, source in available_profiles:
|
76
81
|
normalized_name = name.lower()
|
77
|
-
if
|
82
|
+
if (
|
83
|
+
normalized_input in normalized_name
|
84
|
+
or normalized_name in normalized_input
|
85
|
+
):
|
78
86
|
close_matches.append(name)
|
79
|
-
|
87
|
+
|
80
88
|
suggestion = ""
|
81
89
|
if close_matches:
|
82
90
|
suggestion = f"\nDid you mean: {', '.join(close_matches)}?"
|
83
|
-
|
91
|
+
|
84
92
|
error_msg = f"[janito] Could not find profile '{profile}'. Available profiles:\n{profile_list}{suggestion}"
|
85
93
|
else:
|
86
|
-
error_msg =
|
87
|
-
|
94
|
+
error_msg = (
|
95
|
+
f"[janito] Could not find profile '{profile}'. No profiles available."
|
96
|
+
)
|
97
|
+
|
88
98
|
raise FileNotFoundError(error_msg)
|
89
99
|
# Replace spaces in profile name with underscores for filename resolution
|
90
100
|
sanitized_profile = re.sub(r"\\s+", "_", profile.strip()) if profile else profile
|
@@ -144,20 +154,23 @@ def _prepare_template_context(role, profile, allowed_permissions):
|
|
144
154
|
context["platform"] = pd.get_platform_name()
|
145
155
|
context["python_version"] = pd.get_python_version()
|
146
156
|
context["shell_info"] = pd.detect_shell()
|
147
|
-
|
157
|
+
|
148
158
|
# Add allowed sites for market analyst profile
|
149
159
|
if profile == "market-analyst":
|
150
160
|
from janito.tools.url_whitelist import get_url_whitelist_manager
|
161
|
+
|
151
162
|
whitelist_manager = get_url_whitelist_manager()
|
152
163
|
allowed_sites = whitelist_manager.get_allowed_sites()
|
153
164
|
context["allowed_sites"] = allowed_sites
|
154
|
-
|
165
|
+
|
155
166
|
# Add market data sources documentation
|
156
167
|
if not allowed_sites:
|
157
|
-
context["allowed_sites_info"] =
|
168
|
+
context["allowed_sites_info"] = (
|
169
|
+
"No whitelist restrictions - all sites allowed"
|
170
|
+
)
|
158
171
|
else:
|
159
172
|
context["allowed_sites_info"] = f"Restricted to: {', '.join(allowed_sites)}"
|
160
|
-
|
173
|
+
|
161
174
|
return context
|
162
175
|
|
163
176
|
|
janito/cli/chat_mode/session.py
CHANGED
@@ -116,7 +116,9 @@ class ChatSession:
|
|
116
116
|
def _select_profile_and_role(self, args, role):
|
117
117
|
profile = getattr(args, "profile", None) if args is not None else None
|
118
118
|
role_arg = getattr(args, "role", None) if args is not None else None
|
119
|
-
python_profile =
|
119
|
+
python_profile = (
|
120
|
+
getattr(args, "developer", False) if args is not None else False
|
121
|
+
)
|
120
122
|
market_profile = getattr(args, "market", False) if args is not None else False
|
121
123
|
profile_system_prompt = None
|
122
124
|
no_tools_mode = False
|
@@ -129,26 +131,47 @@ class ChatSession:
|
|
129
131
|
if market_profile and profile is None and role_arg is None:
|
130
132
|
profile = "Market Analyst"
|
131
133
|
|
132
|
-
if
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
134
|
+
if (
|
135
|
+
profile is None
|
136
|
+
and role_arg is None
|
137
|
+
and not python_profile
|
138
|
+
and not market_profile
|
139
|
+
):
|
140
|
+
# Skip interactive profile selection for list commands
|
141
|
+
from janito.cli.core.getters import GETTER_KEYS
|
142
|
+
|
143
|
+
# Check if any getter command is active - these don't need interactive mode
|
144
|
+
skip_profile_selection = False
|
145
|
+
if args is not None:
|
146
|
+
for key in GETTER_KEYS:
|
147
|
+
if getattr(args, key, False):
|
148
|
+
skip_profile_selection = True
|
149
|
+
break
|
150
|
+
|
151
|
+
if skip_profile_selection:
|
152
|
+
profile = "Developer with Python Tools" # Default for non-interactive commands
|
153
|
+
else:
|
154
|
+
try:
|
155
|
+
from janito.cli.chat_mode.session_profile_select import (
|
156
|
+
select_profile,
|
149
157
|
)
|
150
|
-
|
151
|
-
|
158
|
+
|
159
|
+
result = select_profile()
|
160
|
+
if isinstance(result, dict):
|
161
|
+
profile = result.get("profile")
|
162
|
+
profile_system_prompt = result.get("profile_system_prompt")
|
163
|
+
no_tools_mode = result.get("no_tools_mode", False)
|
164
|
+
elif isinstance(result, str) and result.startswith("role:"):
|
165
|
+
role = result[len("role:") :].strip()
|
166
|
+
profile = "Developer with Python Tools"
|
167
|
+
else:
|
168
|
+
profile = (
|
169
|
+
"Developer with Python Tools"
|
170
|
+
if result == "Developer"
|
171
|
+
else result
|
172
|
+
)
|
173
|
+
except ImportError:
|
174
|
+
profile = "Raw Model Session (no tools, no context)"
|
152
175
|
if role_arg is not None:
|
153
176
|
role = role_arg
|
154
177
|
if profile is None:
|
@@ -209,9 +232,7 @@ class ChatSession:
|
|
209
232
|
|
210
233
|
self.console.print(f"[bold green]Janito Chat Mode v{__version__}[/bold green]")
|
211
234
|
self.console.print(f"[dim]Profile: {self.profile}[/dim]")
|
212
|
-
|
213
|
-
"[green]/help for commands /exit or Ctrl+C to quit[/green]"
|
214
|
-
)
|
235
|
+
|
215
236
|
import os
|
216
237
|
|
217
238
|
cwd = os.getcwd()
|
@@ -226,7 +247,7 @@ class ChatSession:
|
|
226
247
|
|
227
248
|
priv_status = get_privilege_status_message()
|
228
249
|
self.console.print(
|
229
|
-
f"[green]Working Dir:[/green] {cwd_display} | {priv_status}"
|
250
|
+
f"[green]Working Dir:[/green] [cyan]{cwd_display}[/cyan] | {priv_status}"
|
230
251
|
)
|
231
252
|
|
232
253
|
if self.multi_line_mode:
|
@@ -282,6 +303,8 @@ class ChatSession:
|
|
282
303
|
|
283
304
|
def _process_prompt(self, cmd_input):
|
284
305
|
try:
|
306
|
+
# Clear screen before processing new prompt
|
307
|
+
self.console.clear()
|
285
308
|
import time
|
286
309
|
|
287
310
|
final_event = (
|
@@ -324,6 +347,7 @@ class ChatSession:
|
|
324
347
|
if top_base:
|
325
348
|
candidates.append(top_base)
|
326
349
|
from urllib.parse import urlparse
|
350
|
+
|
327
351
|
for candidate in candidates:
|
328
352
|
try:
|
329
353
|
if not candidate:
|
@@ -353,6 +377,8 @@ class ChatSession:
|
|
353
377
|
print_token_message_summary(
|
354
378
|
self.console, self.msg_count, usage, elapsed=elapsed
|
355
379
|
)
|
380
|
+
# Send terminal bell character to trigger TUI bell after printing token summary
|
381
|
+
print("\a", end="", flush=True)
|
356
382
|
if final_event and hasattr(final_event, "metadata"):
|
357
383
|
exit_reason = (
|
358
384
|
final_event.metadata.get("exit_reason")
|
@@ -149,7 +149,10 @@ def select_profile():
|
|
149
149
|
|
150
150
|
# Get the absolute path relative to the current script location
|
151
151
|
current_dir = Path(__file__).parent
|
152
|
-
template_path =
|
152
|
+
template_path = (
|
153
|
+
current_dir
|
154
|
+
/ "../../agent/templates/profiles/system_prompt_template_developer.txt.j2"
|
155
|
+
)
|
153
156
|
with open(template_path, "r", encoding="utf-8") as f:
|
154
157
|
template_content = f.read()
|
155
158
|
|
@@ -165,7 +168,10 @@ def select_profile():
|
|
165
168
|
|
166
169
|
# Get the absolute path relative to the current script location
|
167
170
|
current_dir = Path(__file__).parent
|
168
|
-
template_path =
|
171
|
+
template_path = (
|
172
|
+
current_dir
|
173
|
+
/ "../../agent/templates/profiles/system_prompt_template_market_analyst.txt.j2"
|
174
|
+
)
|
169
175
|
with open(template_path, "r", encoding="utf-8") as f:
|
170
176
|
template_content = f.read()
|
171
177
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
from .base import ShellCmdHandler
|
2
2
|
from .history_view import ViewShellHandler
|
3
3
|
from .lang import LangShellHandler
|
4
|
+
from .provider import ProviderCmdHandler
|
4
5
|
|
5
6
|
from .prompt import PromptShellHandler, RoleShellHandler
|
6
7
|
from .multi import MultiShellHandler
|
@@ -43,6 +44,7 @@ COMMAND_HANDLERS = {
|
|
43
44
|
"/multi": MultiShellHandler,
|
44
45
|
"/help": HelpShellHandler,
|
45
46
|
"/security": SecurityCommand,
|
47
|
+
"/provider": ProviderCmdHandler,
|
46
48
|
}
|
47
49
|
|
48
50
|
|
@@ -6,8 +6,10 @@ class ExecuteShellHandler(ShellCmdHandler):
|
|
6
6
|
help_text = "/execute on|off: Enable or disable code and command execution tools. Usage: /execute on or /execute off."
|
7
7
|
|
8
8
|
def run(self):
|
9
|
-
if self.shell_state and getattr(self.shell_state,
|
10
|
-
shared_console.print(
|
9
|
+
if self.shell_state and getattr(self.shell_state, "no_tools_mode", False):
|
10
|
+
shared_console.print(
|
11
|
+
"[yellow]No tools are available in this mode (no tools, no context).[/yellow]"
|
12
|
+
)
|
11
13
|
return
|
12
14
|
if not self.shell_state:
|
13
15
|
shared_console.print("[red]Shell state unavailable.[/red]")
|
@@ -7,7 +7,14 @@ class HelpShellHandler(ShellCmdHandler):
|
|
7
7
|
help_text = "Show this help message"
|
8
8
|
|
9
9
|
def run(self):
|
10
|
-
|
10
|
+
# Ensure /provider command is registered before showing help
|
11
|
+
from janito.cli.chat_mode.shell.commands import COMMAND_HANDLERS
|
12
|
+
|
13
|
+
if "/provider" not in COMMAND_HANDLERS:
|
14
|
+
from janito.cli.chat_mode.shell.commands.provider import ProviderCmdHandler
|
15
|
+
|
16
|
+
COMMAND_HANDLERS["/provider"] = ProviderCmdHandler
|
17
|
+
|
11
18
|
from ._priv_check import user_has_any_privileges
|
12
19
|
|
13
20
|
shared_console.print("[bold magenta]Available commands:[/bold magenta]")
|
@@ -1,16 +1,20 @@
|
|
1
1
|
from janito.cli.console import shared_console
|
2
2
|
from janito.cli.chat_mode.shell.commands.base import ShellCmdHandler
|
3
3
|
|
4
|
+
|
4
5
|
class PrivilegesShellHandler(ShellCmdHandler):
|
5
6
|
help_text = "Show current tool privileges or availability."
|
6
7
|
|
7
8
|
def run(self):
|
8
9
|
# Check for no_tools_mode in shell_state
|
9
|
-
if self.shell_state and getattr(self.shell_state,
|
10
|
-
shared_console.print(
|
10
|
+
if self.shell_state and getattr(self.shell_state, "no_tools_mode", False):
|
11
|
+
shared_console.print(
|
12
|
+
"[yellow]No tools are available in this mode (no tools, no context).[/yellow]"
|
13
|
+
)
|
11
14
|
return
|
12
15
|
try:
|
13
16
|
from janito.tools.permissions import get_global_allowed_permissions
|
17
|
+
|
14
18
|
perms = get_global_allowed_permissions()
|
15
19
|
lines = ["[bold]Current tool privileges:[/bold]"]
|
16
20
|
lines.append(f"Read: {'✅' if perms.read else '❌'}")
|
@@ -0,0 +1,28 @@
|
|
1
|
+
from janito.cli.core.getters import get_current_provider
|
2
|
+
from janito.cli.core.setters import set_provider
|
3
|
+
from janito.cli.chat_mode.shell.commands.base import ShellCmdHandler
|
4
|
+
from janito.cli.console import shared_console as console
|
5
|
+
|
6
|
+
|
7
|
+
class ProviderCmdHandler(ShellCmdHandler):
|
8
|
+
"""Handler for the /provider command to view or change the current provider."""
|
9
|
+
|
10
|
+
help_text = "Manage the current LLM provider. Usage: /provider [provider_name]"
|
11
|
+
|
12
|
+
def run(self):
|
13
|
+
"""Execute the provider command."""
|
14
|
+
if not self.after_cmd_line.strip():
|
15
|
+
# No argument provided, show current provider
|
16
|
+
current = get_current_provider()
|
17
|
+
console.print(f"[bold]Current provider:[/bold] {current}")
|
18
|
+
return
|
19
|
+
|
20
|
+
# Argument provided, attempt to change provider
|
21
|
+
new_provider = self.after_cmd_line.strip()
|
22
|
+
try:
|
23
|
+
set_provider(new_provider)
|
24
|
+
console.print(
|
25
|
+
f"[bold green]Provider changed to:[/bold green] {new_provider}"
|
26
|
+
)
|
27
|
+
except ValueError as e:
|
28
|
+
console.print(f"[bold red]Error:[/bold red] {str(e)}")
|
@@ -6,8 +6,10 @@ class ReadShellHandler(ShellCmdHandler):
|
|
6
6
|
help_text = "/read on|off: Enable or disable read permissions for tools. Usage: /read on or /read off."
|
7
7
|
|
8
8
|
def run(self):
|
9
|
-
if self.shell_state and getattr(self.shell_state,
|
10
|
-
shared_console.print(
|
9
|
+
if self.shell_state and getattr(self.shell_state, "no_tools_mode", False):
|
10
|
+
shared_console.print(
|
11
|
+
"[yellow]No tools are available in this mode (no tools, no context).[/yellow]"
|
12
|
+
)
|
11
13
|
return
|
12
14
|
if not self.shell_state:
|
13
15
|
shared_console.print("[red]Shell state unavailable.[/red]")
|
@@ -1 +1 @@
|
|
1
|
-
"""Security management commands for chat mode."""
|
1
|
+
"""Security management commands for chat mode."""
|
@@ -6,15 +6,17 @@ from janito.tools.url_whitelist import get_url_whitelist_manager
|
|
6
6
|
|
7
7
|
class SecurityAllowedSitesCommand(BaseCommand):
|
8
8
|
"""Manage allowed sites for fetch_url tool."""
|
9
|
-
|
9
|
+
|
10
10
|
def get_name(self) -> str:
|
11
11
|
return "allowed-sites"
|
12
|
-
|
12
|
+
|
13
13
|
def get_description(self) -> str:
|
14
14
|
return "Manage allowed sites for the fetch_url tool"
|
15
|
-
|
15
|
+
|
16
16
|
def get_usage(self):
|
17
|
-
return
|
17
|
+
return (
|
18
|
+
self.get_description()
|
19
|
+
+ """
|
18
20
|
Usage: /security allowed-sites [command] [site]
|
19
21
|
|
20
22
|
Commands:
|
@@ -29,6 +31,7 @@ Examples:
|
|
29
31
|
/security allowed-sites remove yahoo.com
|
30
32
|
/security allowed-sites clear
|
31
33
|
"""
|
34
|
+
)
|
32
35
|
return """
|
33
36
|
Usage: /security allowed-sites [command] [site]
|
34
37
|
|
@@ -44,18 +47,18 @@ Examples:
|
|
44
47
|
/security allowed-sites remove yahoo.com
|
45
48
|
/security allowed-sites clear
|
46
49
|
"""
|
47
|
-
|
50
|
+
|
48
51
|
def run(self):
|
49
52
|
"""Execute the allowed-sites command."""
|
50
53
|
args = self.after_cmd_line.strip().split()
|
51
|
-
|
54
|
+
|
52
55
|
if not args:
|
53
56
|
print(self.get_usage())
|
54
57
|
return
|
55
|
-
|
58
|
+
|
56
59
|
command = args[0].lower()
|
57
60
|
whitelist_manager = get_url_whitelist_manager()
|
58
|
-
|
61
|
+
|
59
62
|
if command == "list":
|
60
63
|
sites = whitelist_manager.get_allowed_sites()
|
61
64
|
if sites:
|
@@ -64,7 +67,7 @@ Examples:
|
|
64
67
|
print(f" • {site}")
|
65
68
|
else:
|
66
69
|
print("No sites are whitelisted (all sites are allowed)")
|
67
|
-
|
70
|
+
|
68
71
|
elif command == "add":
|
69
72
|
if len(args) < 2:
|
70
73
|
print("Error: Please specify a site to add")
|
@@ -74,7 +77,7 @@ Examples:
|
|
74
77
|
print(f"✅ Added '{site}' to allowed sites")
|
75
78
|
else:
|
76
79
|
print(f"ℹ️ '{site}' is already in allowed sites")
|
77
|
-
|
80
|
+
|
78
81
|
elif command == "remove":
|
79
82
|
if len(args) < 2:
|
80
83
|
print("Error: Please specify a site to remove")
|
@@ -84,11 +87,11 @@ Examples:
|
|
84
87
|
print(f"✅ Removed '{site}' from allowed sites")
|
85
88
|
else:
|
86
89
|
print(f"ℹ️ '{site}' was not in allowed sites")
|
87
|
-
|
90
|
+
|
88
91
|
elif command == "clear":
|
89
92
|
whitelist_manager.clear_whitelist()
|
90
93
|
print("✅ Cleared all allowed sites (all sites are now allowed)")
|
91
|
-
|
94
|
+
|
92
95
|
else:
|
93
96
|
print(f"Error: Unknown command '{command}'")
|
94
|
-
print(self.get_usage())
|
97
|
+
print(self.get_usage())
|
@@ -1,18 +1,20 @@
|
|
1
1
|
"""Security command group for chat mode."""
|
2
2
|
|
3
3
|
from janito.cli.chat_mode.shell.commands.base import ShellCmdHandler as BaseCommand
|
4
|
-
from janito.cli.chat_mode.shell.commands.security.allowed_sites import
|
4
|
+
from janito.cli.chat_mode.shell.commands.security.allowed_sites import (
|
5
|
+
SecurityAllowedSitesCommand,
|
6
|
+
)
|
5
7
|
|
6
8
|
|
7
9
|
class SecurityCommand(BaseCommand):
|
8
10
|
"""Security management command group."""
|
9
|
-
|
11
|
+
|
10
12
|
def get_name(self) -> str:
|
11
13
|
return "security"
|
12
|
-
|
14
|
+
|
13
15
|
def get_description(self) -> str:
|
14
16
|
return "Security management commands"
|
15
|
-
|
17
|
+
|
16
18
|
def get_usage(self):
|
17
19
|
return """
|
18
20
|
Usage: /security <subcommand> [args...]
|
@@ -25,21 +27,23 @@ Examples:
|
|
25
27
|
/security allowed-sites add tradingview.com
|
26
28
|
/security allowed-sites remove yahoo.com
|
27
29
|
"""
|
28
|
-
|
30
|
+
|
29
31
|
def __init__(self, after_cmd_line=None, shell_state=None):
|
30
32
|
super().__init__(after_cmd_line=after_cmd_line, shell_state=shell_state)
|
31
33
|
self.subcommands = {
|
32
|
-
"allowed-sites": SecurityAllowedSitesCommand(
|
34
|
+
"allowed-sites": SecurityAllowedSitesCommand(
|
35
|
+
after_cmd_line=after_cmd_line, shell_state=shell_state
|
36
|
+
)
|
33
37
|
}
|
34
|
-
|
38
|
+
|
35
39
|
def run(self):
|
36
40
|
"""Execute the security command."""
|
37
41
|
args = self.after_cmd_line.strip().split()
|
38
|
-
|
42
|
+
|
39
43
|
if not args:
|
40
44
|
print(self.get_usage())
|
41
45
|
return
|
42
|
-
|
46
|
+
|
43
47
|
subcommand = args[0].lower()
|
44
48
|
if subcommand in self.subcommands:
|
45
49
|
# Pass the remaining args to the subcommand
|
@@ -48,4 +52,4 @@ Examples:
|
|
48
52
|
self.subcommands[subcommand].run()
|
49
53
|
else:
|
50
54
|
print(f"Error: Unknown security subcommand '{subcommand}'")
|
51
|
-
print(self.get_usage())
|
55
|
+
print(self.get_usage())
|
@@ -56,8 +56,10 @@ class ToolsShellHandler(ShellCmdHandler):
|
|
56
56
|
|
57
57
|
def run(self):
|
58
58
|
# Check for no_tools_mode in shell_state
|
59
|
-
if self.shell_state and getattr(self.shell_state,
|
60
|
-
shared_console.print(
|
59
|
+
if self.shell_state and getattr(self.shell_state, "no_tools_mode", False):
|
60
|
+
shared_console.print(
|
61
|
+
"[yellow]No tools are available in this mode (no tools, no context).[/yellow]"
|
62
|
+
)
|
61
63
|
return
|
62
64
|
try:
|
63
65
|
import janito.tools # Ensure all tools are registered
|
@@ -6,7 +6,7 @@ from janito.cli.console import shared_console
|
|
6
6
|
|
7
7
|
class UnrestrictedShellHandler(ShellCmdHandler):
|
8
8
|
"""Toggle unrestricted mode (equivalent to -u CLI flag)."""
|
9
|
-
|
9
|
+
|
10
10
|
help_text = "Toggle unrestricted mode (disable path security and URL whitelist)"
|
11
11
|
|
12
12
|
def run(self):
|
@@ -14,27 +14,32 @@ class UnrestrictedShellHandler(ShellCmdHandler):
|
|
14
14
|
if not self.shell_state:
|
15
15
|
shared_console.print("[red]Error: Shell state not available[/red]")
|
16
16
|
return
|
17
|
-
|
17
|
+
|
18
18
|
# Toggle unrestricted mode
|
19
|
-
current_unrestricted = getattr(self.shell_state,
|
19
|
+
current_unrestricted = getattr(self.shell_state, "unrestricted_mode", False)
|
20
20
|
new_unrestricted = not current_unrestricted
|
21
|
-
|
21
|
+
|
22
22
|
# Update shell state
|
23
23
|
self.shell_state.unrestricted_mode = new_unrestricted
|
24
|
-
|
24
|
+
|
25
25
|
# Update tools adapter
|
26
|
-
if hasattr(self.shell_state,
|
27
|
-
setattr(
|
28
|
-
|
26
|
+
if hasattr(self.shell_state, "tools_adapter"):
|
27
|
+
setattr(
|
28
|
+
self.shell_state.tools_adapter, "unrestricted_paths", new_unrestricted
|
29
|
+
)
|
30
|
+
|
29
31
|
# Update URL whitelist manager
|
30
32
|
from janito.tools.url_whitelist import get_url_whitelist_manager
|
33
|
+
|
31
34
|
whitelist_manager = get_url_whitelist_manager()
|
32
35
|
whitelist_manager.set_unrestricted_mode(new_unrestricted)
|
33
|
-
|
36
|
+
|
34
37
|
status = "enabled" if new_unrestricted else "disabled"
|
35
|
-
warning =
|
36
|
-
|
38
|
+
warning = (
|
39
|
+
" (DANGEROUS - no path or URL restrictions)" if new_unrestricted else ""
|
40
|
+
)
|
41
|
+
|
37
42
|
shared_console.print(
|
38
43
|
f"[bold {'red' if new_unrestricted else 'green'}]"
|
39
44
|
f"Unrestricted mode {status}{warning}[/bold {'red' if new_unrestricted else 'green'}]"
|
40
|
-
)
|
45
|
+
)
|
@@ -6,8 +6,10 @@ class WriteShellHandler(ShellCmdHandler):
|
|
6
6
|
help_text = "/write on|off: Enable or disable write permissions for tools. Usage: /write on or /write off."
|
7
7
|
|
8
8
|
def run(self):
|
9
|
-
if self.shell_state and getattr(self.shell_state,
|
10
|
-
shared_console.print(
|
9
|
+
if self.shell_state and getattr(self.shell_state, "no_tools_mode", False):
|
10
|
+
shared_console.print(
|
11
|
+
"[yellow]No tools are available in this mode (no tools, no context).[/yellow]"
|
12
|
+
)
|
11
13
|
return
|
12
14
|
if not self.shell_state:
|
13
15
|
shared_console.print("[red]Shell state unavailable.[/red]")
|