janito 2.27.1__py3-none-any.whl → 2.29.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 +160 -56
- janito/cli/chat_mode/session_profile_select.py +8 -2
- janito/cli/chat_mode/shell/commands/execute.py +4 -2
- janito/cli/chat_mode/shell/commands/help.py +2 -0
- janito/cli/chat_mode/shell/commands/privileges.py +6 -2
- janito/cli/chat_mode/shell/commands/provider.py +7 -4
- 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 +4 -4
- janito/cli/cli_commands/enable_disable_plugin.py +48 -25
- janito/cli/cli_commands/list_models.py +2 -2
- janito/cli/cli_commands/list_plugins.py +18 -18
- 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 +1 -0
- janito/cli/core/model_guesser.py +18 -15
- janito/cli/core/runner.py +15 -7
- janito/cli/core/setters.py +9 -6
- janito/cli/main_cli.py +15 -12
- janito/cli/prompt_setup.py +4 -4
- janito/cli/rich_terminal_reporter.py +2 -1
- 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 +13 -9
- janito/plugins/config.py +16 -19
- janito/plugins/discovery.py +73 -66
- janito/plugins/manager.py +62 -60
- 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/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/validate_file_syntax/jinja2_validator.py +14 -11
- janito/tools/base.py +4 -3
- 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.1.dist-info → janito-2.29.0.dist-info}/METADATA +1 -1
- {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/RECORD +77 -77
- janito/providers/moonshotai/__init__.py +0 -1
- {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/WHEEL +0 -0
- {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/entry_points.txt +0 -0
- {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/licenses/LICENSE +0 -0
- {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/top_level.txt +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
@@ -63,6 +63,7 @@ class ChatSession:
|
|
63
63
|
allowed_permissions=None,
|
64
64
|
):
|
65
65
|
self.console = console
|
66
|
+
self.session_start_time = time.time()
|
66
67
|
self.user_input_history = UserInputHistory()
|
67
68
|
self.input_dicts = self.user_input_history.load()
|
68
69
|
self.mem_history = InMemoryHistory()
|
@@ -114,38 +115,31 @@ class ChatSession:
|
|
114
115
|
self.multi_line_mode = getattr(args, "multi", False) if args else False
|
115
116
|
|
116
117
|
def _select_profile_and_role(self, args, role):
|
117
|
-
profile
|
118
|
-
role_arg = getattr(args, "role", None) if args is not None else None
|
119
|
-
python_profile = getattr(args, "developer", False) if args is not None else False
|
120
|
-
market_profile = getattr(args, "market", False) if args is not None else False
|
118
|
+
profile, role_arg, python_profile, market_profile = self._extract_args(args)
|
121
119
|
profile_system_prompt = None
|
122
120
|
no_tools_mode = False
|
123
121
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
# Handle --market flag
|
129
|
-
if market_profile and profile is None and role_arg is None:
|
130
|
-
profile = "Market Analyst"
|
122
|
+
profile = self._determine_profile(
|
123
|
+
profile, role_arg, python_profile, market_profile
|
124
|
+
)
|
131
125
|
|
132
|
-
if
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
126
|
+
if (
|
127
|
+
profile is None
|
128
|
+
and role_arg is None
|
129
|
+
and not python_profile
|
130
|
+
and not market_profile
|
131
|
+
):
|
132
|
+
skip_profile_selection = self._should_skip_profile_selection(args)
|
133
|
+
else:
|
137
134
|
skip_profile_selection = False
|
138
|
-
|
139
|
-
for key in GETTER_KEYS:
|
140
|
-
if getattr(args, key, False):
|
141
|
-
skip_profile_selection = True
|
142
|
-
break
|
143
|
-
|
135
|
+
|
144
136
|
if skip_profile_selection:
|
145
137
|
profile = "Developer with Python Tools" # Default for non-interactive commands
|
146
138
|
else:
|
147
139
|
try:
|
148
|
-
from janito.cli.chat_mode.session_profile_select import
|
140
|
+
from janito.cli.chat_mode.session_profile_select import (
|
141
|
+
select_profile,
|
142
|
+
)
|
149
143
|
|
150
144
|
result = select_profile()
|
151
145
|
if isinstance(result, dict):
|
@@ -305,39 +299,15 @@ class ChatSession:
|
|
305
299
|
)
|
306
300
|
start_time = time.time()
|
307
301
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
self.agent.get_provider_name()
|
316
|
-
if hasattr(self.agent, "get_provider_name")
|
317
|
-
else "Unknown"
|
302
|
+
model_name, provider_name = self._get_model_info()
|
303
|
+
backend_hostname = self._get_backend_hostname()
|
304
|
+
|
305
|
+
self.console.print(
|
306
|
+
Rule(
|
307
|
+
f"[bold blue]Model: {model_name} ({provider_name}) | Backend: {backend_hostname}[/bold blue]"
|
308
|
+
)
|
318
309
|
)
|
319
310
|
|
320
|
-
backend_hostname = "Unknown"
|
321
|
-
candidates = []
|
322
|
-
drv = getattr(self.agent, "driver", None)
|
323
|
-
if drv is not None:
|
324
|
-
cfg = getattr(drv, "config", None)
|
325
|
-
if cfg is not None:
|
326
|
-
b = getattr(cfg, "base_url", None)
|
327
|
-
if b:
|
328
|
-
candidates.append(b)
|
329
|
-
direct_base = getattr(drv, "base_url", None)
|
330
|
-
if direct_base:
|
331
|
-
candidates.append(direct_base)
|
332
|
-
cfg2 = getattr(self.agent, "config", None)
|
333
|
-
if cfg2 is not None:
|
334
|
-
b2 = getattr(cfg2, "base_url", None)
|
335
|
-
if b2:
|
336
|
-
candidates.append(b2)
|
337
|
-
top_base = getattr(self.agent, "base_url", None)
|
338
|
-
if top_base:
|
339
|
-
candidates.append(top_base)
|
340
|
-
from urllib.parse import urlparse
|
341
311
|
for candidate in candidates:
|
342
312
|
try:
|
343
313
|
if not candidate:
|
@@ -383,6 +353,102 @@ class ChatSession:
|
|
383
353
|
|
384
354
|
self.console.print(traceback.format_exc())
|
385
355
|
|
356
|
+
def _extract_args(self, args):
|
357
|
+
"""Extract profile and role arguments from args."""
|
358
|
+
profile = getattr(args, "profile", None) if args is not None else None
|
359
|
+
role_arg = getattr(args, "role", None) if args is not None else None
|
360
|
+
python_profile = (
|
361
|
+
getattr(args, "developer", False) if args is not None else False
|
362
|
+
)
|
363
|
+
market_profile = getattr(args, "market", False) if args is not None else False
|
364
|
+
return profile, role_arg, python_profile, market_profile
|
365
|
+
|
366
|
+
def _determine_profile(self, profile, role_arg, python_profile, market_profile):
|
367
|
+
"""Determine the profile based on flags and arguments."""
|
368
|
+
if python_profile and profile is None and role_arg is None:
|
369
|
+
return "Developer with Python Tools"
|
370
|
+
if market_profile and profile is None and role_arg is None:
|
371
|
+
return "Market Analyst"
|
372
|
+
return profile
|
373
|
+
|
374
|
+
def _should_skip_profile_selection(self, args):
|
375
|
+
"""Check if profile selection should be skipped for getter commands."""
|
376
|
+
from janito.cli.core.getters import GETTER_KEYS
|
377
|
+
|
378
|
+
if args is None:
|
379
|
+
return False
|
380
|
+
|
381
|
+
for key in GETTER_KEYS:
|
382
|
+
if getattr(args, key, False):
|
383
|
+
return True
|
384
|
+
return False
|
385
|
+
|
386
|
+
def _get_model_info(self):
|
387
|
+
"""Get model and provider information."""
|
388
|
+
model_name = (
|
389
|
+
self.agent.get_model_name()
|
390
|
+
if hasattr(self.agent, "get_model_name")
|
391
|
+
else "Unknown"
|
392
|
+
)
|
393
|
+
provider_name = (
|
394
|
+
self.agent.get_provider_name()
|
395
|
+
if hasattr(self.agent, "get_provider_name")
|
396
|
+
else "Unknown"
|
397
|
+
)
|
398
|
+
return model_name, provider_name
|
399
|
+
|
400
|
+
def _get_backend_hostname(self):
|
401
|
+
"""Extract backend hostname from agent configuration."""
|
402
|
+
candidates = self._collect_base_urls()
|
403
|
+
return self._parse_hostname_from_urls(candidates)
|
404
|
+
|
405
|
+
def _collect_base_urls(self):
|
406
|
+
"""Collect all possible base URLs from agent configuration."""
|
407
|
+
candidates = []
|
408
|
+
|
409
|
+
# Collect from driver
|
410
|
+
drv = getattr(self.agent, "driver", None)
|
411
|
+
if drv is not None:
|
412
|
+
cfg = getattr(drv, "config", None)
|
413
|
+
if cfg is not None:
|
414
|
+
b = getattr(cfg, "base_url", None)
|
415
|
+
if b:
|
416
|
+
candidates.append(b)
|
417
|
+
direct_base = getattr(drv, "base_url", None)
|
418
|
+
if direct_base:
|
419
|
+
candidates.append(direct_base)
|
420
|
+
|
421
|
+
# Collect from agent config
|
422
|
+
cfg2 = getattr(self.agent, "config", None)
|
423
|
+
if cfg2 is not None:
|
424
|
+
b2 = getattr(cfg2, "base_url", None)
|
425
|
+
if b2:
|
426
|
+
candidates.append(b2)
|
427
|
+
|
428
|
+
# Collect from agent directly
|
429
|
+
top_base = getattr(self.agent, "base_url", None)
|
430
|
+
if top_base:
|
431
|
+
candidates.append(top_base)
|
432
|
+
|
433
|
+
return candidates
|
434
|
+
|
435
|
+
def _parse_hostname_from_urls(self, candidates):
|
436
|
+
"""Parse hostname from a list of URL candidates."""
|
437
|
+
from urllib.parse import urlparse
|
438
|
+
|
439
|
+
for candidate in candidates:
|
440
|
+
try:
|
441
|
+
if not candidate:
|
442
|
+
continue
|
443
|
+
parsed = urlparse(str(candidate))
|
444
|
+
host = parsed.netloc or parsed.path
|
445
|
+
if host:
|
446
|
+
return host
|
447
|
+
except Exception:
|
448
|
+
return str(candidate)
|
449
|
+
|
450
|
+
return "Unknown"
|
451
|
+
|
386
452
|
def _create_prompt_session(self):
|
387
453
|
return PromptSession(
|
388
454
|
style=chat_shell_style,
|
@@ -404,7 +470,25 @@ class ChatSession:
|
|
404
470
|
else:
|
405
471
|
try:
|
406
472
|
cmd_input = session.prompt(HTML("<inputline>💬 </inputline>"))
|
407
|
-
except
|
473
|
+
except KeyboardInterrupt:
|
474
|
+
# Ask for confirmation on Ctrl+C
|
475
|
+
from prompt_toolkit import prompt
|
476
|
+
|
477
|
+
try:
|
478
|
+
confirm = prompt(
|
479
|
+
"Are you sure you want to exit? (y/n): ",
|
480
|
+
style=self._create_prompt_session().style,
|
481
|
+
)
|
482
|
+
if confirm.lower() == "y":
|
483
|
+
self._handle_exit()
|
484
|
+
return None
|
485
|
+
else:
|
486
|
+
return "" # Return empty string to continue
|
487
|
+
except (KeyboardInterrupt, EOFError):
|
488
|
+
# Handle second Ctrl+C or Ctrl+D as immediate exit
|
489
|
+
self._handle_exit()
|
490
|
+
return None
|
491
|
+
except EOFError:
|
408
492
|
self._handle_exit()
|
409
493
|
return None
|
410
494
|
sanitized = cmd_input.strip()
|
@@ -418,7 +502,27 @@ class ChatSession:
|
|
418
502
|
return sanitized
|
419
503
|
|
420
504
|
def _handle_exit(self):
|
421
|
-
|
505
|
+
session_duration = time.time() - self.session_start_time
|
506
|
+
|
507
|
+
# Get total token usage from performance collector
|
508
|
+
from janito.perf_singleton import performance_collector
|
509
|
+
|
510
|
+
total_tokens = performance_collector.get_token_usage().get("total_tokens", 0)
|
511
|
+
|
512
|
+
# Format session duration
|
513
|
+
if session_duration < 60:
|
514
|
+
duration_str = f"{session_duration:.1f}s"
|
515
|
+
elif session_duration < 3600:
|
516
|
+
duration_str = f"{session_duration/60:.1f}m"
|
517
|
+
else:
|
518
|
+
duration_str = f"{session_duration/3600:.1f}h"
|
519
|
+
|
520
|
+
self.console.print(f"[bold yellow]Session completed![/bold yellow]")
|
521
|
+
self.console.print(
|
522
|
+
f"[dim]Session time: {duration_str} | Total tokens: {total_tokens:,}[/dim]"
|
523
|
+
)
|
524
|
+
self.console.print("[bold yellow]Goodbye![/bold yellow]")
|
525
|
+
|
422
526
|
if hasattr(self, "agent") and hasattr(self.agent, "join_driver"):
|
423
527
|
if (
|
424
528
|
hasattr(self.agent, "input_queue")
|
@@ -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
|
|
@@ -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]")
|
@@ -9,8 +9,10 @@ class HelpShellHandler(ShellCmdHandler):
|
|
9
9
|
def run(self):
|
10
10
|
# Ensure /provider command is registered before showing help
|
11
11
|
from janito.cli.chat_mode.shell.commands import COMMAND_HANDLERS
|
12
|
+
|
12
13
|
if "/provider" not in COMMAND_HANDLERS:
|
13
14
|
from janito.cli.chat_mode.shell.commands.provider import ProviderCmdHandler
|
15
|
+
|
14
16
|
COMMAND_HANDLERS["/provider"] = ProviderCmdHandler
|
15
17
|
|
16
18
|
from ._priv_check import user_has_any_privileges
|
@@ -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 '❌'}")
|
@@ -3,11 +3,12 @@ from janito.cli.core.setters import set_provider
|
|
3
3
|
from janito.cli.chat_mode.shell.commands.base import ShellCmdHandler
|
4
4
|
from janito.cli.console import shared_console as console
|
5
5
|
|
6
|
+
|
6
7
|
class ProviderCmdHandler(ShellCmdHandler):
|
7
8
|
"""Handler for the /provider command to view or change the current provider."""
|
8
|
-
|
9
|
+
|
9
10
|
help_text = "Manage the current LLM provider. Usage: /provider [provider_name]"
|
10
|
-
|
11
|
+
|
11
12
|
def run(self):
|
12
13
|
"""Execute the provider command."""
|
13
14
|
if not self.after_cmd_line.strip():
|
@@ -15,11 +16,13 @@ class ProviderCmdHandler(ShellCmdHandler):
|
|
15
16
|
current = get_current_provider()
|
16
17
|
console.print(f"[bold]Current provider:[/bold] {current}")
|
17
18
|
return
|
18
|
-
|
19
|
+
|
19
20
|
# Argument provided, attempt to change provider
|
20
21
|
new_provider = self.after_cmd_line.strip()
|
21
22
|
try:
|
22
23
|
set_provider(new_provider)
|
23
|
-
console.print(
|
24
|
+
console.print(
|
25
|
+
f"[bold green]Provider changed to:[/bold green] {new_provider}"
|
26
|
+
)
|
24
27
|
except ValueError as e:
|
25
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())
|