janito 2.25.0__py3-none-any.whl → 2.27.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/cli/chat_mode/shell/commands/unrestricted.py +40 -0
- janito/cli/cli_commands/show_config.py +24 -0
- janito/cli/main_cli.py +13 -1
- janito/tools/adapters/local/fetch_url.py +80 -5
- {janito-2.25.0.dist-info → janito-2.27.0.dist-info}/METADATA +19 -1
- {janito-2.25.0.dist-info → janito-2.27.0.dist-info}/RECORD +10 -9
- {janito-2.25.0.dist-info → janito-2.27.0.dist-info}/WHEEL +0 -0
- {janito-2.25.0.dist-info → janito-2.27.0.dist-info}/entry_points.txt +0 -0
- {janito-2.25.0.dist-info → janito-2.27.0.dist-info}/licenses/LICENSE +0 -0
- {janito-2.25.0.dist-info → janito-2.27.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,40 @@
|
|
1
|
+
"""Unrestricted mode command for chat mode."""
|
2
|
+
|
3
|
+
from janito.cli.chat_mode.shell.commands.base import ShellCmdHandler
|
4
|
+
from janito.cli.console import shared_console
|
5
|
+
|
6
|
+
|
7
|
+
class UnrestrictedShellHandler(ShellCmdHandler):
|
8
|
+
"""Toggle unrestricted mode (equivalent to -u CLI flag)."""
|
9
|
+
|
10
|
+
help_text = "Toggle unrestricted mode (disable path security and URL whitelist)"
|
11
|
+
|
12
|
+
def run(self):
|
13
|
+
"""Execute the unrestricted command."""
|
14
|
+
if not self.shell_state:
|
15
|
+
shared_console.print("[red]Error: Shell state not available[/red]")
|
16
|
+
return
|
17
|
+
|
18
|
+
# Toggle unrestricted mode
|
19
|
+
current_unrestricted = getattr(self.shell_state, 'unrestricted_mode', False)
|
20
|
+
new_unrestricted = not current_unrestricted
|
21
|
+
|
22
|
+
# Update shell state
|
23
|
+
self.shell_state.unrestricted_mode = new_unrestricted
|
24
|
+
|
25
|
+
# Update tools adapter
|
26
|
+
if hasattr(self.shell_state, 'tools_adapter'):
|
27
|
+
setattr(self.shell_state.tools_adapter, 'unrestricted_paths', new_unrestricted)
|
28
|
+
|
29
|
+
# Update URL whitelist manager
|
30
|
+
from janito.tools.url_whitelist import get_url_whitelist_manager
|
31
|
+
whitelist_manager = get_url_whitelist_manager()
|
32
|
+
whitelist_manager.set_unrestricted_mode(new_unrestricted)
|
33
|
+
|
34
|
+
status = "enabled" if new_unrestricted else "disabled"
|
35
|
+
warning = " (DANGEROUS - no path or URL restrictions)" if new_unrestricted else ""
|
36
|
+
|
37
|
+
shared_console.print(
|
38
|
+
f"[bold {'red' if new_unrestricted else 'green'}]"
|
39
|
+
f"Unrestricted mode {status}{warning}[/bold {'red' if new_unrestricted else 'green'}]"
|
40
|
+
)
|
@@ -42,6 +42,30 @@ def handle_show_config(args):
|
|
42
42
|
console.print(f"[bold yellow]Current provider:[/bold yellow] {provider!r}\n")
|
43
43
|
if model is not None:
|
44
44
|
console.print(f"[bold yellow]Global model:[/bold yellow] {model!r}\n")
|
45
|
+
|
46
|
+
# Show all configuration values
|
47
|
+
console.print("[bold green]Configuration values:[/bold green]")
|
48
|
+
all_config = config.all()
|
49
|
+
if all_config:
|
50
|
+
for key, value in sorted(all_config.items()):
|
51
|
+
# Hide sensitive values like API keys
|
52
|
+
if 'api_key' in key.lower() and value:
|
53
|
+
masked_value = value[:8] + '***' + value[-4:] if len(value) > 12 else '***'
|
54
|
+
console.print(f" {key}: {masked_value!r}")
|
55
|
+
elif key == 'providers' and isinstance(value, dict):
|
56
|
+
# Handle nested provider configs with API keys
|
57
|
+
masked_providers = {}
|
58
|
+
for provider_name, provider_config in value.items():
|
59
|
+
masked_config = dict(provider_config)
|
60
|
+
if 'api_key' in masked_config and masked_config['api_key']:
|
61
|
+
api_key = masked_config['api_key']
|
62
|
+
masked_config['api_key'] = api_key[:8] + '***' + api_key[-4:] if len(api_key) > 12 else '***'
|
63
|
+
masked_providers[provider_name] = masked_config
|
64
|
+
console.print(f" {key}: {masked_providers!r}")
|
65
|
+
else:
|
66
|
+
console.print(f" {key}: {value!r}")
|
67
|
+
else:
|
68
|
+
console.print(" (no configuration values set)")
|
45
69
|
|
46
70
|
# Show disabled tools
|
47
71
|
from janito.tools.disabled_tools import load_disabled_tools_from_config
|
janito/cli/main_cli.py
CHANGED
@@ -293,7 +293,8 @@ class JanitoCLI:
|
|
293
293
|
self.parser = argparse.ArgumentParser(
|
294
294
|
description="Janito CLI - A tool for running LLM-powered workflows from the command line."
|
295
295
|
"\n\nExample usage: janito -p moonshotai -m kimi-k1-8k 'Your prompt here'\n\n"
|
296
|
-
"Use -m or --model to set the model for the session."
|
296
|
+
"Use -m or --model to set the model for the session.",
|
297
|
+
|
297
298
|
)
|
298
299
|
self._define_args()
|
299
300
|
self.args = self.parser.parse_args()
|
@@ -409,6 +410,17 @@ class JanitoCLI:
|
|
409
410
|
self._maybe_print_verbose_provider_model()
|
410
411
|
handle_getter(self.args)
|
411
412
|
return
|
413
|
+
# Handle /rwx prefix for enabling all permissions
|
414
|
+
if self.args.user_prompt and self.args.user_prompt[0] == '/rwx':
|
415
|
+
self.args.read = True
|
416
|
+
self.args.write = True
|
417
|
+
self.args.exec = True
|
418
|
+
# Remove the /rwx prefix from the prompt
|
419
|
+
self.args.user_prompt = self.args.user_prompt[1:]
|
420
|
+
elif self.args.user_prompt and self.args.user_prompt[0].startswith('/'):
|
421
|
+
# Skip LLM processing for other commands that start with /
|
422
|
+
return
|
423
|
+
|
412
424
|
# If running in single shot mode and --profile is not provided, default to 'developer' profile
|
413
425
|
if get_prompt_mode(self.args) == "single_shot" and not getattr(
|
414
426
|
self.args, "profile", None
|
@@ -4,6 +4,7 @@ import os
|
|
4
4
|
import json
|
5
5
|
from pathlib import Path
|
6
6
|
from bs4 import BeautifulSoup
|
7
|
+
from typing import Dict, Any, Optional
|
7
8
|
from janito.tools.adapters.local.adapter import register_local_tool
|
8
9
|
from janito.tools.tool_base import ToolBase, ToolPermissions
|
9
10
|
from janito.report_events import ReportAction
|
@@ -45,6 +46,9 @@ class FetchUrlTool(ToolBase):
|
|
45
46
|
timeout (int, optional): Timeout in seconds for the HTTP request. Defaults to 10.
|
46
47
|
save_to_file (str, optional): File path to save the full resource content. If provided,
|
47
48
|
the complete response will be saved to this file instead of being processed.
|
49
|
+
headers (Dict[str, str], optional): Custom HTTP headers to send with the request.
|
50
|
+
cookies (Dict[str, str], optional): Custom cookies to send with the request.
|
51
|
+
follow_redirects (bool, optional): Whether to follow HTTP redirects. Defaults to True.
|
48
52
|
Returns:
|
49
53
|
str: Extracted text content from the web page, or a warning message. Example:
|
50
54
|
- "<main text content...>"
|
@@ -64,6 +68,22 @@ class FetchUrlTool(ToolBase):
|
|
64
68
|
{}
|
65
69
|
) # In-memory session cache - lifetime matches tool instance
|
66
70
|
self._load_cache()
|
71
|
+
|
72
|
+
# Browser-like session with cookies and headers
|
73
|
+
self.session = requests.Session()
|
74
|
+
self.session.headers.update({
|
75
|
+
'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',
|
76
|
+
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
|
77
|
+
'Accept-Language': 'en-US,en;q=0.5',
|
78
|
+
'Accept-Encoding': 'gzip, deflate, br',
|
79
|
+
'DNT': '1',
|
80
|
+
'Connection': 'keep-alive',
|
81
|
+
'Upgrade-Insecure-Requests': '1',
|
82
|
+
})
|
83
|
+
|
84
|
+
# Load cookies from disk if they exist
|
85
|
+
self.cookies_file = self.cache_dir / "cookies.json"
|
86
|
+
self._load_cookies()
|
67
87
|
|
68
88
|
def _load_cache(self):
|
69
89
|
"""Load error cache from disk."""
|
@@ -84,6 +104,33 @@ class FetchUrlTool(ToolBase):
|
|
84
104
|
except IOError:
|
85
105
|
pass # Silently fail if we can't write cache
|
86
106
|
|
107
|
+
def _load_cookies(self):
|
108
|
+
"""Load cookies from disk into session."""
|
109
|
+
if self.cookies_file.exists():
|
110
|
+
try:
|
111
|
+
with open(self.cookies_file, "r", encoding="utf-8") as f:
|
112
|
+
cookies_data = json.load(f)
|
113
|
+
for cookie in cookies_data:
|
114
|
+
self.session.cookies.set(**cookie)
|
115
|
+
except (json.JSONDecodeError, IOError):
|
116
|
+
pass # Silently fail if we can't load cookies
|
117
|
+
|
118
|
+
def _save_cookies(self):
|
119
|
+
"""Save session cookies to disk."""
|
120
|
+
try:
|
121
|
+
cookies_data = []
|
122
|
+
for cookie in self.session.cookies:
|
123
|
+
cookies_data.append({
|
124
|
+
'name': cookie.name,
|
125
|
+
'value': cookie.value,
|
126
|
+
'domain': cookie.domain,
|
127
|
+
'path': cookie.path
|
128
|
+
})
|
129
|
+
with open(self.cookies_file, "w", encoding="utf-8") as f:
|
130
|
+
json.dump(cookies_data, f, indent=2)
|
131
|
+
except IOError:
|
132
|
+
pass # Silently fail if we can't write cookies
|
133
|
+
|
87
134
|
def _get_cached_error(self, url: str) -> tuple[str, bool]:
|
88
135
|
"""
|
89
136
|
Check if we have a cached error for this URL.
|
@@ -123,14 +170,15 @@ class FetchUrlTool(ToolBase):
|
|
123
170
|
}
|
124
171
|
self._save_cache()
|
125
172
|
|
126
|
-
def _fetch_url_content(self, url: str, timeout: int = 10
|
173
|
+
def _fetch_url_content(self, url: str, timeout: int = 10, headers: Optional[Dict[str, str]] = None,
|
174
|
+
cookies: Optional[Dict[str, str]] = None, follow_redirects: bool = True) -> str:
|
127
175
|
"""Fetch URL content and handle HTTP errors.
|
128
176
|
|
129
177
|
Implements two-tier caching:
|
130
178
|
1. Session cache: In-memory cache for successful responses (lifetime = tool instance)
|
131
179
|
2. Error cache: Persistent disk cache for HTTP errors with different expiration times
|
132
180
|
|
133
|
-
Also implements URL whitelist checking.
|
181
|
+
Also implements URL whitelist checking and browser-like behavior.
|
134
182
|
"""
|
135
183
|
# Check URL whitelist
|
136
184
|
from janito.tools.url_whitelist import get_url_whitelist_manager
|
@@ -172,9 +220,27 @@ class FetchUrlTool(ToolBase):
|
|
172
220
|
return cached_error
|
173
221
|
|
174
222
|
try:
|
175
|
-
|
223
|
+
# Merge custom headers with default ones
|
224
|
+
request_headers = self.session.headers.copy()
|
225
|
+
if headers:
|
226
|
+
request_headers.update(headers)
|
227
|
+
|
228
|
+
# Merge custom cookies
|
229
|
+
if cookies:
|
230
|
+
self.session.cookies.update(cookies)
|
231
|
+
|
232
|
+
response = self.session.get(
|
233
|
+
url,
|
234
|
+
timeout=timeout,
|
235
|
+
headers=request_headers,
|
236
|
+
allow_redirects=follow_redirects
|
237
|
+
)
|
176
238
|
response.raise_for_status()
|
177
239
|
content = response.text
|
240
|
+
|
241
|
+
# Save cookies after successful request
|
242
|
+
self._save_cookies()
|
243
|
+
|
178
244
|
# Cache successful responses in session cache
|
179
245
|
self.session_cache[url] = content
|
180
246
|
return content
|
@@ -275,6 +341,9 @@ class FetchUrlTool(ToolBase):
|
|
275
341
|
context_chars: int = 400,
|
276
342
|
timeout: int = 10,
|
277
343
|
save_to_file: str = None,
|
344
|
+
headers: Dict[str, str] = None,
|
345
|
+
cookies: Dict[str, str] = None,
|
346
|
+
follow_redirects: bool = True,
|
278
347
|
) -> str:
|
279
348
|
if not url.strip():
|
280
349
|
self.report_warning(tr("ℹ️ Empty URL provided."), ReportAction.READ)
|
@@ -284,7 +353,10 @@ class FetchUrlTool(ToolBase):
|
|
284
353
|
|
285
354
|
# Check if we should save to file
|
286
355
|
if save_to_file:
|
287
|
-
html_content = self._fetch_url_content(
|
356
|
+
html_content = self._fetch_url_content(
|
357
|
+
url, timeout=timeout, headers=headers, cookies=cookies,
|
358
|
+
follow_redirects=follow_redirects
|
359
|
+
)
|
288
360
|
if html_content.startswith("Warning:"):
|
289
361
|
return html_content
|
290
362
|
|
@@ -307,7 +379,10 @@ class FetchUrlTool(ToolBase):
|
|
307
379
|
return error_msg
|
308
380
|
|
309
381
|
# Normal processing path
|
310
|
-
html_content = self._fetch_url_content(
|
382
|
+
html_content = self._fetch_url_content(
|
383
|
+
url, timeout=timeout, headers=headers, cookies=cookies,
|
384
|
+
follow_redirects=follow_redirects
|
385
|
+
)
|
311
386
|
if html_content.startswith("Warning:"):
|
312
387
|
return html_content
|
313
388
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: janito
|
3
|
-
Version: 2.
|
3
|
+
Version: 2.27.0
|
4
4
|
Summary: A new Python package called janito.
|
5
5
|
Author-email: João Pinto <janito@ikignosis.org>
|
6
6
|
Project-URL: Homepage, https://github.com/ikignosis/janito
|
@@ -187,7 +187,25 @@ janito -r -w "Read and update this file: ..."
|
|
187
187
|
To enable all permissions (read, write, execute):
|
188
188
|
|
189
189
|
```bash
|
190
|
+
# Using individual flags
|
190
191
|
janito -r -w -x "Run this code: print('Hello, world!')"
|
192
|
+
|
193
|
+
# Using the convenient /rwx prefix (single-shot mode)
|
194
|
+
janito /rwx "Run this code: print('Hello, world!')"
|
195
|
+
```
|
196
|
+
|
197
|
+
#### One-Shot Mode
|
198
|
+
For quick tasks without entering interactive mode, provide your prompt directly:
|
199
|
+
|
200
|
+
```bash
|
201
|
+
# Basic one-shot
|
202
|
+
janito "What are the key classes in this project?"
|
203
|
+
|
204
|
+
# One-shot with all permissions enabled
|
205
|
+
janito /rwx "Create a Python script and run it"
|
206
|
+
|
207
|
+
# One-shot with specific permissions
|
208
|
+
janito -r -w "Read this file and create a summary"
|
191
209
|
```
|
192
210
|
|
193
211
|
> **Warning:** Enabling execution tools allows running arbitrary code or shell commands. Only use `--exec` if you trust your prompt and environment.
|
@@ -29,7 +29,7 @@ janito/cli/__init__.py,sha256=xaPDOrWphBbCR63Xpcx_yfpXSJIlCaaICc4j2qpWqrM,194
|
|
29
29
|
janito/cli/config.py,sha256=HkZ14701HzIqrvaNyDcDhGlVHfpX_uHlLp2rHmhRm_k,872
|
30
30
|
janito/cli/console.py,sha256=gJolqzWL7jEPLxeuH-CwBDRFpXt976KdZOEAB2tdBDs,64
|
31
31
|
janito/cli/main.py,sha256=s5odou0txf8pzTf1ADk2yV7T5m8B6cejJ81e7iu776U,312
|
32
|
-
janito/cli/main_cli.py,sha256=
|
32
|
+
janito/cli/main_cli.py,sha256=VusBQQd9tpeBWPzERVobYCeR75A96W_1VnEWCX2R1Xo,16927
|
33
33
|
janito/cli/prompt_core.py,sha256=F68J4Xl6jZMYFN4oBBYZFj15Jp-HTYoLub4bw2XpNRU,11648
|
34
34
|
janito/cli/prompt_handler.py,sha256=SnPTlL64noeAMGlI08VBDD5IDD8jlVMIYA4-fS8zVLg,215
|
35
35
|
janito/cli/prompt_setup.py,sha256=1SSvvgS568-3BO_4Sw9A-QF_iLWiIXsNHT0JVqaLwkU,2120
|
@@ -68,6 +68,7 @@ janito/cli/chat_mode/shell/commands/security_command.py,sha256=7Xgjb8Gunk9CjKxz1
|
|
68
68
|
janito/cli/chat_mode/shell/commands/session.py,sha256=9wsw5U24-KJgTT70KAwnGuS8MVPyvpxCJfAt_Io76O0,1574
|
69
69
|
janito/cli/chat_mode/shell/commands/session_control.py,sha256=tmMGJ8IvWoBwSIJu7T6GPnFYFrmXWIG2Gr9tTni4y3U,1307
|
70
70
|
janito/cli/chat_mode/shell/commands/tools.py,sha256=8BWgqB0j5sp7mYupxoAjYvLWjb_wODOfCk-I8yNfk-U,3473
|
71
|
+
janito/cli/chat_mode/shell/commands/unrestricted.py,sha256=cVlQ0o-Zq0ll1ZMCq5TLm52fAee44TwwiRen0kN6hWU,1699
|
71
72
|
janito/cli/chat_mode/shell/commands/utility.py,sha256=K982P-UwgPLzpEvSuSBGwiQ3zC34S_6T2ork_djweQw,847
|
72
73
|
janito/cli/chat_mode/shell/commands/verbose.py,sha256=HDTe0NhdcjBuhh-eJSW8iLPDeeBO95K5iSDAMAljxa4,953
|
73
74
|
janito/cli/chat_mode/shell/commands/write.py,sha256=bpvJzHY7wlpgsB-cpslzfQQl7h6n-NLqj2gFCuJfjVk,2309
|
@@ -88,7 +89,7 @@ janito/cli/cli_commands/model_selection.py,sha256=ANWtwC5glZkGMdaNtARDbEG3QmuBUc
|
|
88
89
|
janito/cli/cli_commands/model_utils.py,sha256=PO64PW-TIlWyPY8CzYnI0y5Zp6ukV_NjaSj8CEXflV0,4889
|
89
90
|
janito/cli/cli_commands/ping_providers.py,sha256=U3iTETPXDEGr3dqlJqNTPvpkhKo2KfXe_bN2_LGJtx0,2015
|
90
91
|
janito/cli/cli_commands/set_api_key.py,sha256=ZItSuB0HO14UsbyXXCgTKAXS-EUCHfCkntzg3WAAtK0,1048
|
91
|
-
janito/cli/cli_commands/show_config.py,sha256=
|
92
|
+
janito/cli/cli_commands/show_config.py,sha256=UnitVtjYqR0XR2viAFaeiODqkfUwJA5xdkqfQa7kr_0,3313
|
92
93
|
janito/cli/cli_commands/show_system_prompt.py,sha256=YKOVHKzz6pzwvJGlqVEK2R4i98HGrUZHsrsFNvBBEd8,5898
|
93
94
|
janito/cli/core/__init__.py,sha256=YH95fhgY9yBX8RgqX9dSrEkl4exjV0T4rbmJ6xUpG-Y,196
|
94
95
|
janito/cli/core/event_logger.py,sha256=1X6lR0Ax7AgF8HlPWFoY5Ystuu7Bh4ooTo78vXzeGB0,2008
|
@@ -203,7 +204,7 @@ janito/tools/adapters/local/copy_file.py,sha256=SBJm19Ipe5dqRE1Mxl6JSrn4bNmfObVn
|
|
203
204
|
janito/tools/adapters/local/create_directory.py,sha256=LxwqQEsnOrEphCIoaMRRx9P9bu0MzidP3Fc5q6letxc,2584
|
204
205
|
janito/tools/adapters/local/create_file.py,sha256=Y3cSYg0nWt2HGPj0j4Ut25joSkecsXOcw3OUc28IZCg,3764
|
205
206
|
janito/tools/adapters/local/delete_text_in_file.py,sha256=uEeedRxXAR7_CqUc_qhbEdM0OzRi_pgnP-iDjs2Zvjk,5087
|
206
|
-
janito/tools/adapters/local/fetch_url.py,sha256=
|
207
|
+
janito/tools/adapters/local/fetch_url.py,sha256=dDcgyCBnUw5zG6gLEon0Mu4GfucUbK0tN9xtH6jBaIg,16296
|
207
208
|
janito/tools/adapters/local/find_files.py,sha256=Zbag3aP34vc7ffJh8bOqAwXj3KiZhV--uzTVHtNb-fI,6250
|
208
209
|
janito/tools/adapters/local/move_file.py,sha256=LMGm8bn3NNyIPJG4vrlO09smXQcgzA09EwoooZxkIA8,4695
|
209
210
|
janito/tools/adapters/local/open_html_in_browser.py,sha256=XqICIwVx5vEE77gHkaNAC-bAeEEy0DBmDksATiL-sRY,2101
|
@@ -242,14 +243,14 @@ janito/tools/adapters/local/validate_file_syntax/ps1_validator.py,sha256=TeIkPt0
|
|
242
243
|
janito/tools/adapters/local/validate_file_syntax/python_validator.py,sha256=BfCO_K18qy92m-2ZVvHsbEU5e11OPo1pO9Vz4G4616E,130
|
243
244
|
janito/tools/adapters/local/validate_file_syntax/xml_validator.py,sha256=AijlsP_PgNuC8ZbGsC5vOTt3Jur76otQzkd_7qR0QFY,284
|
244
245
|
janito/tools/adapters/local/validate_file_syntax/yaml_validator.py,sha256=TgyI0HRL6ug_gBcWEm5TGJJuA4E34ZXcIzMpAbv3oJs,155
|
245
|
-
janito-2.
|
246
|
+
janito-2.27.0.dist-info/licenses/LICENSE,sha256=GSAKapQH5ZIGWlpQTA7v5YrfECyaxaohUb1vJX-qepw,1090
|
246
247
|
janito-coder/pyproject.toml,sha256=HPJgGe6YJ4CV93YxD8DYw_A18F51MW-iqweCXLVFko4,12990
|
247
248
|
janito-coder/janito_coder/__init__.py,sha256=QbCJXOHgiXXohtJeUc8frav8mee12ve-eUifpACndrY,198
|
248
249
|
janito-coder/janito_coder/plugins/__init__.py,sha256=NdjMhAEu8FEnVxEkYAN-Cz17Cx2qbMq8dMZMOQwBc-k,844
|
249
250
|
janito-coder/janito_coder/plugins/code_navigator.py,sha256=9f8mt713yXVjuM4RFXrR2Y1ibznOYOaLyeTqfnt2wvE,22101
|
250
251
|
janito-coder/janito_coder/plugins/git_analyzer.py,sha256=DEF_6J8z1-_Fshl1RAesz5b-aYC2nhf7COla44FdntE,8398
|
251
|
-
janito-2.
|
252
|
-
janito-2.
|
253
|
-
janito-2.
|
254
|
-
janito-2.
|
255
|
-
janito-2.
|
252
|
+
janito-2.27.0.dist-info/METADATA,sha256=sFUAedoBtF62qhiKZeA36nQsGKqUp0Qjpy3kV1Us2P8,16875
|
253
|
+
janito-2.27.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
254
|
+
janito-2.27.0.dist-info/entry_points.txt,sha256=wIo5zZxbmu4fC-ZMrsKD0T0vq7IqkOOLYhrqRGypkx4,48
|
255
|
+
janito-2.27.0.dist-info/top_level.txt,sha256=rL2_EzEekHIjO_EteQ1nOywm0B9Ywb0ZeqSKtzZMGUc,20
|
256
|
+
janito-2.27.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|