janito 2.27.1__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 +17 -5
- 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_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 +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/search_text/core.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.28.0.dist-info}/METADATA +1 -1
- {janito-2.27.1.dist-info → janito-2.28.0.dist-info}/RECORD +79 -79
- janito/providers/moonshotai/__init__.py +0 -1
- {janito-2.27.1.dist-info → janito-2.28.0.dist-info}/WHEEL +0 -0
- {janito-2.27.1.dist-info → janito-2.28.0.dist-info}/entry_points.txt +0 -0
- {janito-2.27.1.dist-info → janito-2.28.0.dist-info}/licenses/LICENSE +0 -0
- {janito-2.27.1.dist-info → janito-2.28.0.dist-info}/top_level.txt +0 -0
@@ -12,7 +12,7 @@ MODEL_SPECS = {
|
|
12
12
|
max_cot=8192,
|
13
13
|
),
|
14
14
|
"qwen-plus": LLMModelInfo(
|
15
|
-
name="qwen-plus",
|
15
|
+
name="qwen-plus",
|
16
16
|
context=131072,
|
17
17
|
max_response=8192,
|
18
18
|
category="Alibaba Qwen Plus Model (OpenAI-compatible)",
|
@@ -23,7 +23,7 @@ MODEL_SPECS = {
|
|
23
23
|
),
|
24
24
|
"qwen-max": LLMModelInfo(
|
25
25
|
name="qwen-max",
|
26
|
-
context=32768,
|
26
|
+
context=32768,
|
27
27
|
max_response=8192,
|
28
28
|
category="Alibaba Qwen Max Model (OpenAI-compatible)",
|
29
29
|
driver="OpenAIModelDriver",
|
@@ -31,7 +31,6 @@ MODEL_SPECS = {
|
|
31
31
|
thinking=False,
|
32
32
|
max_cot=8192,
|
33
33
|
),
|
34
|
-
|
35
34
|
"qwen3-coder-plus": LLMModelInfo(
|
36
35
|
name="qwen3-coder-plus",
|
37
36
|
context=1048576,
|
@@ -52,7 +51,6 @@ MODEL_SPECS = {
|
|
52
51
|
thinking=False,
|
53
52
|
max_cot=65536,
|
54
53
|
),
|
55
|
-
|
56
54
|
# Qwen3 1M context models (July 2025 update)
|
57
55
|
"qwen3-235b-a22b-thinking-2507": LLMModelInfo(
|
58
56
|
name="qwen3-235b-a22b-thinking-2507",
|
@@ -94,4 +92,4 @@ MODEL_SPECS = {
|
|
94
92
|
thinking=False,
|
95
93
|
max_cot=32768,
|
96
94
|
),
|
97
|
-
}
|
95
|
+
}
|
@@ -17,7 +17,9 @@ class AlibabaProvider(LLMProvider):
|
|
17
17
|
NAME = "alibaba"
|
18
18
|
MAINTAINER = "João Pinto <janito@ikignosis.org>"
|
19
19
|
MODEL_SPECS = MODEL_SPECS
|
20
|
-
DEFAULT_MODEL =
|
20
|
+
DEFAULT_MODEL = (
|
21
|
+
"qwen3-235b-a22b-instruct-2507" # 129k context, general-purpose model
|
22
|
+
)
|
21
23
|
|
22
24
|
def __init__(
|
23
25
|
self, auth_manager: LLMAuthManager = None, config: LLMDriverConfig = None
|
@@ -1 +1 @@
|
|
1
|
-
# Cerebras provider package
|
1
|
+
# Cerebras provider package
|
@@ -11,11 +11,8 @@ MODEL_SPECS = {
|
|
11
11
|
driver="CerebrasModelDriver",
|
12
12
|
other={
|
13
13
|
"description": "Qwen 3 32B model for general instruction following",
|
14
|
-
"pricing": {
|
15
|
-
|
16
|
-
"output_per_1k_tokens": 0.0006
|
17
|
-
}
|
18
|
-
}
|
14
|
+
"pricing": {"input_per_1k_tokens": 0.0002, "output_per_1k_tokens": 0.0006},
|
15
|
+
},
|
19
16
|
),
|
20
17
|
"qwen-3-235b-a22b-instruct-2507": LLMModelInfo(
|
21
18
|
name="qwen-3-235b-a22b-instruct-2507",
|
@@ -25,11 +22,8 @@ MODEL_SPECS = {
|
|
25
22
|
driver="CerebrasModelDriver",
|
26
23
|
other={
|
27
24
|
"description": "Qwen 3 235B A22B instruction-tuned model (preview)",
|
28
|
-
"pricing": {
|
29
|
-
|
30
|
-
"output_per_1k_tokens": 0.003
|
31
|
-
}
|
32
|
-
}
|
25
|
+
"pricing": {"input_per_1k_tokens": 0.001, "output_per_1k_tokens": 0.003},
|
26
|
+
},
|
33
27
|
),
|
34
28
|
"qwen-3-235b-a22b-thinking-2507": LLMModelInfo(
|
35
29
|
name="qwen-3-235b-a22b-thinking-2507",
|
@@ -39,11 +33,8 @@ MODEL_SPECS = {
|
|
39
33
|
driver="CerebrasModelDriver",
|
40
34
|
other={
|
41
35
|
"description": "Qwen 3 235B A22B thinking model for reasoning tasks (preview)",
|
42
|
-
"pricing": {
|
43
|
-
|
44
|
-
"output_per_1k_tokens": 0.003
|
45
|
-
}
|
46
|
-
}
|
36
|
+
"pricing": {"input_per_1k_tokens": 0.001, "output_per_1k_tokens": 0.003},
|
37
|
+
},
|
47
38
|
),
|
48
39
|
"qwen-3-coder-480b": LLMModelInfo(
|
49
40
|
name="qwen-3-coder-480b",
|
@@ -53,11 +44,8 @@ MODEL_SPECS = {
|
|
53
44
|
driver="CerebrasModelDriver",
|
54
45
|
other={
|
55
46
|
"description": "Qwen 3 Coder 480B model for programming tasks (preview)",
|
56
|
-
"pricing": {
|
57
|
-
|
58
|
-
"output_per_1k_tokens": 0.006
|
59
|
-
}
|
60
|
-
}
|
47
|
+
"pricing": {"input_per_1k_tokens": 0.002, "output_per_1k_tokens": 0.006},
|
48
|
+
},
|
61
49
|
),
|
62
50
|
"gpt-oss-120b": LLMModelInfo(
|
63
51
|
name="gpt-oss-120b",
|
@@ -67,10 +55,7 @@ MODEL_SPECS = {
|
|
67
55
|
driver="CerebrasModelDriver",
|
68
56
|
other={
|
69
57
|
"description": "GPT-OSS 120B open-source model (preview)",
|
70
|
-
"pricing": {
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
}
|
75
|
-
)
|
76
|
-
}
|
58
|
+
"pricing": {"input_per_1k_tokens": 0.0008, "output_per_1k_tokens": 0.0024},
|
59
|
+
},
|
60
|
+
),
|
61
|
+
}
|
@@ -12,14 +12,16 @@ from .model_info import MODEL_SPECS
|
|
12
12
|
|
13
13
|
class CerebrasProvider(LLMProvider):
|
14
14
|
"""Cerebras Inference API provider."""
|
15
|
-
|
15
|
+
|
16
16
|
name = "cerebras"
|
17
17
|
NAME = "cerebras"
|
18
18
|
DEFAULT_MODEL = "qwen-3-coder-480b"
|
19
19
|
MAINTAINER = "João Pinto <janito@ikignosis.org>"
|
20
20
|
MODEL_SPECS = MODEL_SPECS
|
21
21
|
|
22
|
-
def __init__(
|
22
|
+
def __init__(
|
23
|
+
self, auth_manager: LLMAuthManager = None, config: LLMDriverConfig = None
|
24
|
+
):
|
23
25
|
"""Initialize Cerebras provider with optional configuration."""
|
24
26
|
super().__init__()
|
25
27
|
self._tools_adapter = get_local_tools_adapter()
|
@@ -32,10 +34,10 @@ class CerebrasProvider(LLMProvider):
|
|
32
34
|
self._initialize_config(auth_manager, config)
|
33
35
|
self._setup_model_config()
|
34
36
|
self.fill_missing_device_info(self._driver_config)
|
35
|
-
|
37
|
+
|
36
38
|
if not self.available:
|
37
39
|
return
|
38
|
-
|
40
|
+
|
39
41
|
self._initialize_config(None, None)
|
40
42
|
self._driver_config.base_url = "https://api.cerebras.ai/v1"
|
41
43
|
|
@@ -72,11 +74,11 @@ class CerebrasProvider(LLMProvider):
|
|
72
74
|
# Set context length
|
73
75
|
if hasattr(model_spec, "context") and model_spec.context:
|
74
76
|
self._driver_config.context_length = model_spec.context
|
75
|
-
|
77
|
+
|
76
78
|
# Set max tokens based on model spec
|
77
79
|
if hasattr(model_spec, "max_response") and model_spec.max_response:
|
78
80
|
self._driver_config.max_tokens = model_spec.max_response
|
79
|
-
|
81
|
+
|
80
82
|
# Set max completion tokens if thinking is supported
|
81
83
|
if getattr(model_spec, "thinking_supported", False):
|
82
84
|
max_cot = getattr(model_spec, "max_cot", None)
|
@@ -130,10 +132,10 @@ class CerebrasProvider(LLMProvider):
|
|
130
132
|
name: model_info.to_dict()
|
131
133
|
for name, model_info in self.MODEL_SPECS.items()
|
132
134
|
}
|
133
|
-
|
135
|
+
|
134
136
|
if model_name in self.MODEL_SPECS:
|
135
137
|
return self.MODEL_SPECS[model_name].to_dict()
|
136
|
-
|
138
|
+
|
137
139
|
return None
|
138
140
|
|
139
141
|
def execute_tool(self, tool_name: str, event_bus, *args, **kwargs):
|
@@ -142,4 +144,4 @@ class CerebrasProvider(LLMProvider):
|
|
142
144
|
|
143
145
|
|
144
146
|
# Register the provider
|
145
|
-
LLMProviderRegistry.register(CerebrasProvider.name, CerebrasProvider)
|
147
|
+
LLMProviderRegistry.register(CerebrasProvider.name, CerebrasProvider)
|
@@ -1 +1 @@
|
|
1
|
-
# Codestral provider module
|
1
|
+
# Codestral provider module
|
@@ -0,0 +1 @@
|
|
1
|
+
# Moonshot provider package
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from janito.llm.model import LLMModelInfo
|
2
2
|
|
3
|
-
|
3
|
+
MOONSHOT_MODEL_SPECS = {
|
4
4
|
"kimi-k2-0711-preview": LLMModelInfo(
|
5
5
|
name="kimi-k2-0711-preview",
|
6
6
|
context=128000,
|
@@ -9,7 +9,7 @@ MOONSHOTAI_MODEL_SPECS = {
|
|
9
9
|
max_response=4096,
|
10
10
|
thinking_supported=False,
|
11
11
|
default_temp=0.2,
|
12
|
-
open="
|
12
|
+
open="moonshot",
|
13
13
|
driver="OpenAIModelDriver",
|
14
14
|
),
|
15
15
|
"kimi-k2-turbo-preview": LLMModelInfo(
|
@@ -20,7 +20,7 @@ MOONSHOTAI_MODEL_SPECS = {
|
|
20
20
|
max_response=4096,
|
21
21
|
thinking_supported=False,
|
22
22
|
default_temp=0.2,
|
23
|
-
open="
|
23
|
+
open="moonshot",
|
24
24
|
driver="OpenAIModelDriver",
|
25
25
|
),
|
26
26
|
}
|
@@ -4,14 +4,14 @@ from janito.llm.driver_config import LLMDriverConfig
|
|
4
4
|
from janito.drivers.openai.driver import OpenAIModelDriver
|
5
5
|
from janito.tools import get_local_tools_adapter
|
6
6
|
from janito.providers.registry import LLMProviderRegistry
|
7
|
-
from .model_info import
|
7
|
+
from .model_info import MOONSHOT_MODEL_SPECS
|
8
8
|
|
9
9
|
|
10
|
-
class
|
11
|
-
name = "
|
12
|
-
NAME = "
|
10
|
+
class MoonshotProvider(LLMProvider):
|
11
|
+
name = "moonshot"
|
12
|
+
NAME = "moonshot"
|
13
13
|
MAINTAINER = "João Pinto <janito@ikignosis.org>"
|
14
|
-
MODEL_SPECS =
|
14
|
+
MODEL_SPECS = MOONSHOT_MODEL_SPECS
|
15
15
|
DEFAULT_MODEL = "kimi-k2-turbo-preview"
|
16
16
|
|
17
17
|
def __init__(
|
@@ -34,7 +34,7 @@ class MoonshotAIProvider(LLMProvider):
|
|
34
34
|
if not self._api_key:
|
35
35
|
from janito.llm.auth_utils import handle_missing_api_key
|
36
36
|
|
37
|
-
handle_missing_api_key(self.name, "
|
37
|
+
handle_missing_api_key(self.name, "MOONSHOT_API_KEY")
|
38
38
|
|
39
39
|
self._driver_config = config or LLMDriverConfig(model=None)
|
40
40
|
if not self._driver_config.model:
|
@@ -69,7 +69,7 @@ class MoonshotAIProvider(LLMProvider):
|
|
69
69
|
def driver(self) -> OpenAIModelDriver:
|
70
70
|
if not self.available:
|
71
71
|
raise ImportError(
|
72
|
-
f"
|
72
|
+
f"MoonshotProvider unavailable: {self.unavailable_reason}"
|
73
73
|
)
|
74
74
|
return self._driver
|
75
75
|
|
@@ -101,4 +101,4 @@ class MoonshotAIProvider(LLMProvider):
|
|
101
101
|
return self._tools_adapter.execute_by_name(tool_name, *args, **kwargs)
|
102
102
|
|
103
103
|
|
104
|
-
LLMProviderRegistry.register(
|
104
|
+
LLMProviderRegistry.register(MoonshotProvider.NAME, MoonshotProvider)
|
@@ -17,7 +17,9 @@ class OpenAIProvider(LLMProvider):
|
|
17
17
|
NAME = "openai"
|
18
18
|
MAINTAINER = "João Pinto <janito@ikignosis.org>"
|
19
19
|
MODEL_SPECS = MODEL_SPECS
|
20
|
-
DEFAULT_MODEL =
|
20
|
+
DEFAULT_MODEL = (
|
21
|
+
"gpt-4.1" # Options: gpt-4.1, gpt-4o, o3-mini, o4-mini, gpt-5, gpt-5-nano
|
22
|
+
)
|
21
23
|
|
22
24
|
def __init__(
|
23
25
|
self, auth_manager: LLMAuthManager = None, config: LLMDriverConfig = None
|
janito/report_events.py
CHANGED
@@ -25,7 +25,7 @@ class CreateFileTool(ToolBase):
|
|
25
25
|
- "✅ Successfully created the file at ..."
|
26
26
|
|
27
27
|
Note: Syntax validation is automatically performed after this operation.
|
28
|
-
|
28
|
+
|
29
29
|
Security: This tool includes loop protection to prevent excessive file creation operations.
|
30
30
|
Maximum 5 calls per 10 seconds for the same file path.
|
31
31
|
"""
|
@@ -68,19 +68,21 @@ class FetchUrlTool(ToolBase):
|
|
68
68
|
{}
|
69
69
|
) # In-memory session cache - lifetime matches tool instance
|
70
70
|
self._load_cache()
|
71
|
-
|
71
|
+
|
72
72
|
# Browser-like session with cookies and headers
|
73
73
|
self.session = requests.Session()
|
74
|
-
self.session.headers.update(
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
74
|
+
self.session.headers.update(
|
75
|
+
{
|
76
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
77
|
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
78
|
+
"Accept-Language": "en-US,en;q=0.5",
|
79
|
+
"Accept-Encoding": "gzip, deflate, br",
|
80
|
+
"DNT": "1",
|
81
|
+
"Connection": "keep-alive",
|
82
|
+
"Upgrade-Insecure-Requests": "1",
|
83
|
+
}
|
84
|
+
)
|
85
|
+
|
84
86
|
# Load cookies from disk if they exist
|
85
87
|
self.cookies_file = self.cache_dir / "cookies.json"
|
86
88
|
self._load_cookies()
|
@@ -120,12 +122,14 @@ class FetchUrlTool(ToolBase):
|
|
120
122
|
try:
|
121
123
|
cookies_data = []
|
122
124
|
for cookie in self.session.cookies:
|
123
|
-
cookies_data.append(
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
125
|
+
cookies_data.append(
|
126
|
+
{
|
127
|
+
"name": cookie.name,
|
128
|
+
"value": cookie.value,
|
129
|
+
"domain": cookie.domain,
|
130
|
+
"path": cookie.path,
|
131
|
+
}
|
132
|
+
)
|
129
133
|
with open(self.cookies_file, "w", encoding="utf-8") as f:
|
130
134
|
json.dump(cookies_data, f, indent=2)
|
131
135
|
except IOError:
|
@@ -170,8 +174,14 @@ class FetchUrlTool(ToolBase):
|
|
170
174
|
}
|
171
175
|
self._save_cache()
|
172
176
|
|
173
|
-
def _fetch_url_content(
|
174
|
-
|
177
|
+
def _fetch_url_content(
|
178
|
+
self,
|
179
|
+
url: str,
|
180
|
+
timeout: int = 10,
|
181
|
+
headers: Optional[Dict[str, str]] = None,
|
182
|
+
cookies: Optional[Dict[str, str]] = None,
|
183
|
+
follow_redirects: bool = True,
|
184
|
+
) -> str:
|
175
185
|
"""Fetch URL content and handle HTTP errors.
|
176
186
|
|
177
187
|
Implements two-tier caching:
|
@@ -224,23 +234,23 @@ class FetchUrlTool(ToolBase):
|
|
224
234
|
request_headers = self.session.headers.copy()
|
225
235
|
if headers:
|
226
236
|
request_headers.update(headers)
|
227
|
-
|
237
|
+
|
228
238
|
# Merge custom cookies
|
229
239
|
if cookies:
|
230
240
|
self.session.cookies.update(cookies)
|
231
241
|
|
232
242
|
response = self.session.get(
|
233
|
-
url,
|
234
|
-
timeout=timeout,
|
243
|
+
url,
|
244
|
+
timeout=timeout,
|
235
245
|
headers=request_headers,
|
236
|
-
allow_redirects=follow_redirects
|
246
|
+
allow_redirects=follow_redirects,
|
237
247
|
)
|
238
248
|
response.raise_for_status()
|
239
249
|
content = response.text
|
240
|
-
|
250
|
+
|
241
251
|
# Save cookies after successful request
|
242
252
|
self._save_cookies()
|
243
|
-
|
253
|
+
|
244
254
|
# Cache successful responses in session cache
|
245
255
|
self.session_cache[url] = content
|
246
256
|
return content
|
@@ -354,8 +364,11 @@ class FetchUrlTool(ToolBase):
|
|
354
364
|
# Check if we should save to file
|
355
365
|
if save_to_file:
|
356
366
|
html_content = self._fetch_url_content(
|
357
|
-
url,
|
358
|
-
|
367
|
+
url,
|
368
|
+
timeout=timeout,
|
369
|
+
headers=headers,
|
370
|
+
cookies=cookies,
|
371
|
+
follow_redirects=follow_redirects,
|
359
372
|
)
|
360
373
|
if html_content.startswith("Warning:"):
|
361
374
|
return html_content
|
@@ -380,8 +393,11 @@ class FetchUrlTool(ToolBase):
|
|
380
393
|
|
381
394
|
# Normal processing path
|
382
395
|
html_content = self._fetch_url_content(
|
383
|
-
url,
|
384
|
-
|
396
|
+
url,
|
397
|
+
timeout=timeout,
|
398
|
+
headers=headers,
|
399
|
+
cookies=cookies,
|
400
|
+
follow_redirects=follow_redirects,
|
385
401
|
)
|
386
402
|
if html_content.startswith("Warning:"):
|
387
403
|
return html_content
|
@@ -32,7 +32,8 @@ class PythonCommandRunTool(ToolBase):
|
|
32
32
|
return tr("Warning: Empty code provided. Operation skipped.")
|
33
33
|
if not silent:
|
34
34
|
self.report_action(
|
35
|
-
tr("🐍 Running: python -c ...\n{code}\n", code=code),
|
35
|
+
tr("🐍 Running: python -c ...\n{code}\n", code=code),
|
36
|
+
ReportAction.EXECUTE,
|
36
37
|
)
|
37
38
|
self.report_stdout("\n")
|
38
39
|
else:
|
@@ -43,7 +43,7 @@ class RunPowershellCommandTool(ToolBase):
|
|
43
43
|
if require_confirmation:
|
44
44
|
self.report_warning(
|
45
45
|
tr("⚠️ Confirmation requested, but no handler (auto-confirmed)."),
|
46
|
-
ReportAction.EXECUTE
|
46
|
+
ReportAction.EXECUTE,
|
47
47
|
)
|
48
48
|
return True # Auto-confirm for now
|
49
49
|
return True
|
@@ -98,7 +98,7 @@ class SearchTextTool(ToolBase):
|
|
98
98
|
if max_depth > 0:
|
99
99
|
info_str += tr(" [max_depth={max_depth}]", max_depth=max_depth)
|
100
100
|
if count_only:
|
101
|
-
info_str += " [count]"
|
101
|
+
info_str += " [count-only]"
|
102
102
|
self.report_action(info_str, ReportAction.READ)
|
103
103
|
if os.path.isfile(search_path):
|
104
104
|
dir_output, dir_limit_reached, per_file_counts = self._handle_file(
|
@@ -8,40 +8,43 @@ def validate_jinja2(path: str) -> str:
|
|
8
8
|
"""Validate Jinja2 template syntax."""
|
9
9
|
try:
|
10
10
|
from jinja2 import Environment, TemplateSyntaxError
|
11
|
-
|
11
|
+
|
12
12
|
with open(path, "r", encoding="utf-8") as f:
|
13
13
|
content = f.read()
|
14
|
-
|
14
|
+
|
15
15
|
# Create a Jinja2 environment and try to parse the template
|
16
16
|
env = Environment()
|
17
17
|
try:
|
18
18
|
env.parse(content)
|
19
19
|
return tr("✅ Syntax OK")
|
20
20
|
except TemplateSyntaxError as e:
|
21
|
-
line_num = getattr(e,
|
22
|
-
return tr(
|
23
|
-
|
21
|
+
line_num = getattr(e, "lineno", 0)
|
22
|
+
return tr(
|
23
|
+
"⚠️ Warning: Syntax error: {error} at line {line}",
|
24
|
+
error=str(e),
|
25
|
+
line=line_num,
|
26
|
+
)
|
24
27
|
except Exception as e:
|
25
28
|
return tr("⚠️ Warning: Syntax error: {error}", error=str(e))
|
26
|
-
|
29
|
+
|
27
30
|
except ImportError:
|
28
31
|
# If jinja2 is not available, just check basic structure
|
29
32
|
try:
|
30
33
|
with open(path, "r", encoding="utf-8") as f:
|
31
34
|
content = f.read()
|
32
|
-
|
35
|
+
|
33
36
|
# Basic checks for common Jinja2 syntax issues
|
34
37
|
open_tags = content.count("{%")
|
35
38
|
close_tags = content.count("%}")
|
36
39
|
open_vars = content.count("{{")
|
37
40
|
close_vars = content.count("}}")
|
38
|
-
|
41
|
+
|
39
42
|
if open_tags != close_tags:
|
40
43
|
return tr("⚠️ Warning: Syntax error: Mismatched Jinja2 tags")
|
41
44
|
if open_vars != close_vars:
|
42
45
|
return tr("⚠️ Warning: Syntax error: Mismatched Jinja2 variables")
|
43
|
-
|
46
|
+
|
44
47
|
return tr("✅ Syntax OK (basic validation)")
|
45
|
-
|
48
|
+
|
46
49
|
except Exception as e:
|
47
|
-
return tr("⚠️ Warning: Syntax error: {error}", error=str(e))
|
50
|
+
return tr("⚠️ Warning: Syntax error: {error}", error=str(e))
|
janito/tools/base.py
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
class BaseTool:
|
2
2
|
"""Base class for all tools."""
|
3
|
+
|
3
4
|
tool_name: str = ""
|
4
|
-
|
5
|
+
|
5
6
|
def __init__(self):
|
6
7
|
if not self.tool_name:
|
7
8
|
self.tool_name = self.__class__.__name__.lower()
|
8
|
-
|
9
|
+
|
9
10
|
def run(self, *args, **kwargs) -> str:
|
10
11
|
"""Execute the tool."""
|
11
|
-
raise NotImplementedError
|
12
|
+
raise NotImplementedError
|