janito 2.31.1__py3-none-any.whl → 2.33.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/agent/setup_agent.py +26 -0
- janito/agent/templates/profiles/system_prompt_template_Developer_with_Python_Tools.txt.j2 +2 -0
- janito/agent/templates/profiles/system_prompt_template_developer.txt.j2 +2 -0
- janito/agent/templates/profiles/system_prompt_template_market_analyst.txt.j2 +2 -0
- janito/agent/templates/profiles/system_prompt_template_model_conversation_without_tools_or_context.txt.j2 +2 -0
- janito/providers/deepseek/model_info.py +21 -0
- janito/providers/deepseek/provider.py +1 -1
- janito/tools/adapters/local/fetch_url.py +45 -27
- janito/tools/adapters/local/show_image.py +7 -3
- janito/tools/adapters/local/show_image_grid.py +5 -4
- {janito-2.31.1.dist-info → janito-2.33.0.dist-info}/METADATA +1 -1
- {janito-2.31.1.dist-info → janito-2.33.0.dist-info}/RECORD +16 -16
- {janito-2.31.1.dist-info → janito-2.33.0.dist-info}/WHEEL +0 -0
- {janito-2.31.1.dist-info → janito-2.33.0.dist-info}/entry_points.txt +0 -0
- {janito-2.31.1.dist-info → janito-2.33.0.dist-info}/licenses/LICENSE +0 -0
- {janito-2.31.1.dist-info → janito-2.33.0.dist-info}/top_level.txt +0 -0
janito/agent/setup_agent.py
CHANGED
@@ -155,6 +155,32 @@ def _prepare_template_context(role, profile, allowed_permissions, args=None):
|
|
155
155
|
getattr(args, "emoji", False) if "args" in locals() else False
|
156
156
|
)
|
157
157
|
|
158
|
+
# Add current date/time with timezone using standard library
|
159
|
+
from datetime import datetime, timezone
|
160
|
+
import time
|
161
|
+
|
162
|
+
# Get local time with timezone info
|
163
|
+
local_time = datetime.now()
|
164
|
+
|
165
|
+
# Get timezone offset
|
166
|
+
if time.daylight:
|
167
|
+
offset = time.altzone
|
168
|
+
else:
|
169
|
+
offset = time.timezone
|
170
|
+
|
171
|
+
# Format offset as +HHMM or -HHMM
|
172
|
+
offset_hours = -offset // 3600
|
173
|
+
offset_minutes = abs(offset) % 3600 // 60
|
174
|
+
offset_str = f"{offset_hours:+03d}{offset_minutes:02d}"
|
175
|
+
|
176
|
+
# Get timezone name
|
177
|
+
tz_name = time.tzname[time.daylight and time.daylight or 0]
|
178
|
+
|
179
|
+
context["current_datetime"] = local_time.strftime(
|
180
|
+
f"%Y-%m-%d %H:%M:%S {tz_name}{offset_str}"
|
181
|
+
)
|
182
|
+
context["timezone"] = f"{tz_name} (UTC{offset_str})"
|
183
|
+
|
158
184
|
return context
|
159
185
|
|
160
186
|
|
@@ -4,6 +4,8 @@
|
|
4
4
|
{% if description %}Description: {{ description }}{% endif %}
|
5
5
|
{% if author %}Author: {{ author }}{% endif %}
|
6
6
|
{% if tags %}Tags: {{ tags | join(', ') }}{% endif %}
|
7
|
+
{% if current_datetime %}Current Date/Time: {{ current_datetime }}{% endif %}
|
8
|
+
{% if timezone %}Timezone: {{ timezone }}{% endif %}
|
7
9
|
|
8
10
|
{# General role setup
|
9
11
|
ex. "Search in code" -> Python Developer -> find(*.py) | Java Developer -> find(*.java)
|
@@ -4,6 +4,8 @@
|
|
4
4
|
{% if description %}Description: {{ description }}{% endif %}
|
5
5
|
{% if author %}Author: {{ author }}{% endif %}
|
6
6
|
{% if tags %}Tags: {{ tags | join(', ') }}{% endif %}
|
7
|
+
{% if current_datetime %}Current Date/Time: {{ current_datetime }}{% endif %}
|
8
|
+
{% if timezone %}Timezone: {{ timezone }}{% endif %}
|
7
9
|
|
8
10
|
You are: {{ role | default('software developer') }}
|
9
11
|
|
@@ -4,6 +4,8 @@
|
|
4
4
|
{% if description %}Description: {{ description }}{% endif %}
|
5
5
|
{% if author %}Author: {{ author }}{% endif %}
|
6
6
|
{% if tags %}Tags: {{ tags | join(', ') }}{% endif %}
|
7
|
+
{% if current_datetime %}Current Date/Time: {{ current_datetime }}{% endif %}
|
8
|
+
{% if timezone %}Timezone: {{ timezone }}{% endif %}
|
7
9
|
|
8
10
|
You are: {{ role | default('market analyst specializing in financial markets, business intelligence, and economic research') }}
|
9
11
|
|
@@ -4,6 +4,8 @@
|
|
4
4
|
{% if description %}Description: {{ description }}{% endif %}
|
5
5
|
{% if author %}Author: {{ author }}{% endif %}
|
6
6
|
{% if tags %}Tags: {{ tags | join(', ') }}{% endif %}
|
7
|
+
{% if current_datetime %}Current Date/Time: {{ current_datetime }}{% endif %}
|
8
|
+
{% if timezone %}Timezone: {{ timezone }}{% endif %}
|
7
9
|
|
8
10
|
You are: {{ role | default('helpful assistant') }}
|
9
11
|
|
@@ -13,4 +13,25 @@ MODEL_SPECS = {
|
|
13
13
|
"family": "deepseek",
|
14
14
|
"default": False,
|
15
15
|
},
|
16
|
+
"deepseek-v3.1": {
|
17
|
+
"description": "DeepSeek V3.1 Model (128K context, OpenAI-compatible)",
|
18
|
+
"context_window": 131072,
|
19
|
+
"max_tokens": 4096,
|
20
|
+
"family": "deepseek",
|
21
|
+
"default": False,
|
22
|
+
},
|
23
|
+
"deepseek-v3.1-base": {
|
24
|
+
"description": "DeepSeek V3.1 Base Model (128K context, OpenAI-compatible)",
|
25
|
+
"context_window": 131072,
|
26
|
+
"max_tokens": 4096,
|
27
|
+
"family": "deepseek",
|
28
|
+
"default": False,
|
29
|
+
},
|
30
|
+
"deepseek-r1": {
|
31
|
+
"description": "DeepSeek R1 Model (128K context, OpenAI-compatible)",
|
32
|
+
"context_window": 131072,
|
33
|
+
"max_tokens": 4096,
|
34
|
+
"family": "deepseek",
|
35
|
+
"default": False,
|
36
|
+
},
|
16
37
|
}
|
@@ -17,7 +17,7 @@ class DeepSeekProvider(LLMProvider):
|
|
17
17
|
NAME = "deepseek"
|
18
18
|
MAINTAINER = "João Pinto <janito@ikignosis.org>"
|
19
19
|
MODEL_SPECS = MODEL_SPECS
|
20
|
-
DEFAULT_MODEL = "deepseek-chat" # Options: deepseek-chat, deepseek-reasoner
|
20
|
+
DEFAULT_MODEL = "deepseek-chat" # Options: deepseek-chat, deepseek-reasoner, deepseek-v3.1, deepseek-v3.1-base, deepseek-r1
|
21
21
|
|
22
22
|
def __init__(
|
23
23
|
self, auth_manager: LLMAuthManager = None, config: LLMDriverConfig = None
|
@@ -246,11 +246,11 @@ class FetchUrlTool(ToolBase):
|
|
246
246
|
return content
|
247
247
|
except requests.exceptions.HTTPError as http_err:
|
248
248
|
status_code = http_err.response.status_code if http_err.response else None
|
249
|
-
|
249
|
+
|
250
250
|
# Map status codes to descriptions
|
251
251
|
status_descriptions = {
|
252
252
|
400: "Bad Request",
|
253
|
-
401: "Unauthorized",
|
253
|
+
401: "Unauthorized",
|
254
254
|
403: "Forbidden",
|
255
255
|
404: "Not Found",
|
256
256
|
405: "Method Not Allowed",
|
@@ -266,50 +266,68 @@ class FetchUrlTool(ToolBase):
|
|
266
266
|
502: "Bad Gateway",
|
267
267
|
503: "Service Unavailable",
|
268
268
|
504: "Gateway Timeout",
|
269
|
-
505: "HTTP Version Not Supported"
|
269
|
+
505: "HTTP Version Not Supported",
|
270
270
|
}
|
271
|
-
|
271
|
+
|
272
272
|
if status_code and 400 <= status_code < 500:
|
273
273
|
description = status_descriptions.get(status_code, "Client Error")
|
274
|
-
error_message =
|
275
|
-
"HTTP Error {status_code} {description}",
|
276
|
-
status_code=status_code,
|
277
|
-
description=description,
|
278
|
-
)
|
274
|
+
error_message = f"HTTP {status_code} {description}"
|
279
275
|
# Cache 403 and 404 errors
|
280
276
|
if status_code in [403, 404]:
|
281
277
|
self._cache_error(url, status_code, error_message)
|
282
278
|
|
283
279
|
self.report_error(
|
284
|
-
|
285
|
-
"❗ HTTP Error {status_code} {description}",
|
286
|
-
status_code=status_code,
|
287
|
-
description=description,
|
288
|
-
),
|
280
|
+
f"❗ HTTP {status_code} {description}",
|
289
281
|
ReportAction.READ,
|
290
282
|
)
|
291
283
|
return error_message
|
292
|
-
|
293
|
-
description = status_descriptions.get(status_code, "Server Error")
|
284
|
+
elif status_code and 500 <= status_code < 600:
|
285
|
+
description = status_descriptions.get(status_code, "Server Error")
|
286
|
+
error_message = f"HTTP {status_code} {description}"
|
294
287
|
self.report_error(
|
295
|
-
|
296
|
-
"❗ HTTP Error {status_code} {description}",
|
297
|
-
status_code=status_code or "Error",
|
298
|
-
description=description,
|
299
|
-
),
|
288
|
+
f"❗ HTTP {status_code} {description}",
|
300
289
|
ReportAction.READ,
|
301
290
|
)
|
302
|
-
return
|
303
|
-
|
304
|
-
|
305
|
-
|
291
|
+
return error_message
|
292
|
+
else:
|
293
|
+
status_code_str = str(status_code) if status_code else "Error"
|
294
|
+
description = status_descriptions.get(
|
295
|
+
status_code,
|
296
|
+
(
|
297
|
+
"Server Error"
|
298
|
+
if status_code and status_code >= 500
|
299
|
+
else "Client Error"
|
300
|
+
),
|
306
301
|
)
|
302
|
+
self.report_error(
|
303
|
+
f"❗ HTTP {status_code_str} {description}",
|
304
|
+
ReportAction.READ,
|
305
|
+
)
|
306
|
+
return f"HTTP {status_code_str} {description}"
|
307
|
+
except requests.exceptions.ConnectionError as conn_err:
|
308
|
+
self.report_error(
|
309
|
+
"❗ Network Error",
|
310
|
+
ReportAction.READ,
|
311
|
+
)
|
312
|
+
return f"Network Error: Failed to connect to {url}"
|
313
|
+
except requests.exceptions.Timeout as timeout_err:
|
314
|
+
self.report_error(
|
315
|
+
"❗ Timeout Error",
|
316
|
+
ReportAction.READ,
|
317
|
+
)
|
318
|
+
return f"Timeout Error: Request timed out after {timeout} seconds"
|
319
|
+
except requests.exceptions.RequestException as req_err:
|
320
|
+
self.report_error(
|
321
|
+
"❗ Request Error",
|
322
|
+
ReportAction.READ,
|
323
|
+
)
|
324
|
+
return f"Request Error: {str(req_err)}"
|
307
325
|
except Exception as err:
|
308
326
|
self.report_error(
|
309
|
-
|
327
|
+
"❗ Error fetching URL",
|
310
328
|
ReportAction.READ,
|
311
329
|
)
|
312
|
-
return
|
330
|
+
return f"Error: {str(err)}"
|
313
331
|
|
314
332
|
def _extract_and_clean_text(self, html_content: str) -> str:
|
315
333
|
"""Extract and clean text from HTML content."""
|
@@ -36,9 +36,9 @@ class ShowImageTool(ToolBase):
|
|
36
36
|
|
37
37
|
try:
|
38
38
|
from rich.console import Console
|
39
|
-
from
|
39
|
+
from PIL import Image as PILImage
|
40
40
|
except Exception as e:
|
41
|
-
msg = tr("⚠️ Missing dependency:
|
41
|
+
msg = tr("⚠️ Missing dependency: PIL/Pillow ({error})", error=e)
|
42
42
|
self.report_error(msg)
|
43
43
|
return msg
|
44
44
|
|
@@ -53,7 +53,11 @@ class ShowImageTool(ToolBase):
|
|
53
53
|
|
54
54
|
try:
|
55
55
|
console = Console()
|
56
|
-
|
56
|
+
from rich.console import Console
|
57
|
+
from rich.text import Text
|
58
|
+
console = Console()
|
59
|
+
img = PILImage.open(path)
|
60
|
+
console.print(Text(f"Image: {disp_path} ({img.width}x{img.height})", style="bold green"))
|
57
61
|
console.print(img)
|
58
62
|
self.report_success(tr("✅ Displayed"))
|
59
63
|
details = []
|
@@ -40,10 +40,10 @@ class ShowImageGridTool(ToolBase):
|
|
40
40
|
try:
|
41
41
|
from rich.console import Console
|
42
42
|
from rich.columns import Columns
|
43
|
-
from
|
43
|
+
from PIL import Image as PILImage
|
44
44
|
from rich.panel import Panel
|
45
45
|
except Exception as e:
|
46
|
-
msg = tr("⚠️ Missing dependency:
|
46
|
+
msg = tr("⚠️ Missing dependency: PIL/Pillow ({error})", error=e)
|
47
47
|
self.report_error(msg)
|
48
48
|
return msg
|
49
49
|
|
@@ -61,8 +61,9 @@ class ShowImageGridTool(ToolBase):
|
|
61
61
|
self.report_warning(tr("❗ not found: {p}", p=display_path(fp)))
|
62
62
|
continue
|
63
63
|
try:
|
64
|
-
img =
|
65
|
-
|
64
|
+
img = PILImage.open(fp)
|
65
|
+
title = f"{display_path(fp)} ({img.width}x{img.height})"
|
66
|
+
images.append(Panel.fit(title, title=display_path(fp), border_style="dim"))
|
66
67
|
shown += 1
|
67
68
|
except Exception as e:
|
68
69
|
self.report_warning(tr("⚠️ Skipped {p}: {e}", p=display_path(fp), e=e))
|
@@ -20,11 +20,11 @@ janito/provider_registry.py,sha256=IRNB35Cjn4PSXMWOxKBjPg0DfUEOoL4vh63OSPxhMtk,6
|
|
20
20
|
janito/report_events.py,sha256=q4OR_jTZNfcqaQF_fzTjgqo6_VlUIxSGWfhpT4nJWcw,938
|
21
21
|
janito/shell.bak.zip,sha256=hznHbmgfkAkjuQDJ3w73XPQh05yrtUZQxLmtGbanbYU,22
|
22
22
|
janito/utils.py,sha256=eXSsMgM69YyzahgCNrJQLcEbB8ssLI1MQqaa20ONxbE,376
|
23
|
-
janito/agent/setup_agent.py,sha256=
|
24
|
-
janito/agent/templates/profiles/system_prompt_template_Developer_with_Python_Tools.txt.j2,sha256=
|
25
|
-
janito/agent/templates/profiles/system_prompt_template_developer.txt.j2,sha256=
|
26
|
-
janito/agent/templates/profiles/system_prompt_template_market_analyst.txt.j2,sha256=
|
27
|
-
janito/agent/templates/profiles/system_prompt_template_model_conversation_without_tools_or_context.txt.j2,sha256=
|
23
|
+
janito/agent/setup_agent.py,sha256=DIc8gcX41CHWg6YGVSqQJuQxl1iUYk9JIR07wTvHJyk,13012
|
24
|
+
janito/agent/templates/profiles/system_prompt_template_Developer_with_Python_Tools.txt.j2,sha256=gA_Br5UoM-CS8eyoYUtoPz4v2IXCjBcaRQmZ-chEEAI,4064
|
25
|
+
janito/agent/templates/profiles/system_prompt_template_developer.txt.j2,sha256=6cKS-UJ8pJX3voGsMFqMrJWCCYH0aX8ZWcToRCeEbI4,3747
|
26
|
+
janito/agent/templates/profiles/system_prompt_template_market_analyst.txt.j2,sha256=V9JAxdZcWkl53rmSRnrJCjHIsRM6GWEsNLno2hdKx7M,4256
|
27
|
+
janito/agent/templates/profiles/system_prompt_template_model_conversation_without_tools_or_context.txt.j2,sha256=Zgoa26AlR37rjYAPl-4ydA3NdHm8ylT831IaMiGTd5o,1722
|
28
28
|
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
|
@@ -157,8 +157,8 @@ janito/providers/cerebras/__init__.py,sha256=w7VvVDG-AmW7axvt80TSM_HvAM7MjtH_2yM
|
|
157
157
|
janito/providers/cerebras/model_info.py,sha256=kfIYk9ypKyrSSBCtyXZejDGexSkerBqNhaU1_sQQPKw,2176
|
158
158
|
janito/providers/cerebras/provider.py,sha256=2O-L5pNxOVX0MfB2muUoLgj6BO-lNfwlILm1sAV_bpk,5615
|
159
159
|
janito/providers/deepseek/__init__.py,sha256=4sISEpq4bJO29vxFK9cfnO-SRUmKoD7oSdeCvz0hVno,36
|
160
|
-
janito/providers/deepseek/model_info.py,sha256=
|
161
|
-
janito/providers/deepseek/provider.py,sha256=
|
160
|
+
janito/providers/deepseek/model_info.py,sha256=CzpdcUjDSjx43REcf9TydI5MrFM7quljXfEThZYo2C4,1176
|
161
|
+
janito/providers/deepseek/provider.py,sha256=EmNT4GfkE_zevEalwxy6HmoTb6eD1f3UgroRzYIQ85Y,4154
|
162
162
|
janito/providers/google/__init__.py,sha256=hE3OGJvLEhvNLhIK_XmCGIdrIj8MKlyGgdOLJ4mdess,38
|
163
163
|
janito/providers/google/model_info.py,sha256=AakTmzvWm1GPvFzGAq6-PeE_Dpq7BmAAqmh3L8N5KKo,1126
|
164
164
|
janito/providers/google/provider.py,sha256=NQVG5kovHOc2SDgWjVIwYGMqshvMUAqRAk9iMntQ52k,3606
|
@@ -214,7 +214,7 @@ janito/tools/adapters/local/copy_file.py,sha256=SBJm19Ipe5dqRE1Mxl6JSrn4bNmfObVn
|
|
214
214
|
janito/tools/adapters/local/create_directory.py,sha256=LxwqQEsnOrEphCIoaMRRx9P9bu0MzidP3Fc5q6letxc,2584
|
215
215
|
janito/tools/adapters/local/create_file.py,sha256=fLTVnMpDnHzzIVK3nS0DtawBT-I18Is-qa0Hg8y6TXY,6385
|
216
216
|
janito/tools/adapters/local/delete_text_in_file.py,sha256=uEeedRxXAR7_CqUc_qhbEdM0OzRi_pgnP-iDjs2Zvjk,5087
|
217
|
-
janito/tools/adapters/local/fetch_url.py,sha256=
|
217
|
+
janito/tools/adapters/local/fetch_url.py,sha256=QjoOZj06PjsU8uFjJ6O8Fy-WUvS4GhSaHAJQx2rAzoE,18212
|
218
218
|
janito/tools/adapters/local/find_files.py,sha256=Zbag3aP34vc7ffJh8bOqAwXj3KiZhV--uzTVHtNb-fI,6250
|
219
219
|
janito/tools/adapters/local/move_file.py,sha256=LMGm8bn3NNyIPJG4vrlO09smXQcgzA09EwoooZxkIA8,4695
|
220
220
|
janito/tools/adapters/local/open_html_in_browser.py,sha256=XqICIwVx5vEE77gHkaNAC-bAeEEy0DBmDksATiL-sRY,2101
|
@@ -229,8 +229,8 @@ janito/tools/adapters/local/remove_file.py,sha256=Imra4jGkBfAd6pnUAmbUsjN0exj2vz
|
|
229
229
|
janito/tools/adapters/local/replace_text_in_file.py,sha256=zJIDecviF2YRpWxbvhtka4Iaje-QYhcaqQX1PxWolzE,10966
|
230
230
|
janito/tools/adapters/local/run_bash_command.py,sha256=7fABqAeAu7WJwzzwHmT54_m5OSwPMcgpQ74lQhPG7TA,7955
|
231
231
|
janito/tools/adapters/local/run_powershell_command.py,sha256=uQSJVQe40wSGbesyvZxDmIKJthAbDJFaxXm1dEN3gBs,9313
|
232
|
-
janito/tools/adapters/local/show_image.py,sha256=
|
233
|
-
janito/tools/adapters/local/show_image_grid.py,sha256=
|
232
|
+
janito/tools/adapters/local/show_image.py,sha256=OkdiIkaRhjqPMSF5QFcqjLK-fN1oljo_YQM9TH2ZK_A,2932
|
233
|
+
janito/tools/adapters/local/show_image_grid.py,sha256=w_U04_K0qmCcX8ac6KUcs-UsnfCb-qsXT3-kasr17dM,3000
|
234
234
|
janito/tools/adapters/local/view_file.py,sha256=cBKcbwbfH-UMyvQ7PmYTgsshcFmorjWtyH1kaYi7oNY,7379
|
235
235
|
janito/tools/adapters/local/get_file_outline/__init__.py,sha256=OKV_BHnoD9h-vkcVoW6AHmsuDjjauHPCKNK0nVFl4sU,37
|
236
236
|
janito/tools/adapters/local/get_file_outline/core.py,sha256=25k6a8PaDYxAfxnEAvZvOWg2BgUcgKU_BkzJmZKUMEA,4611
|
@@ -255,9 +255,9 @@ janito/tools/adapters/local/validate_file_syntax/ps1_validator.py,sha256=TeIkPt0
|
|
255
255
|
janito/tools/adapters/local/validate_file_syntax/python_validator.py,sha256=BfCO_K18qy92m-2ZVvHsbEU5e11OPo1pO9Vz4G4616E,130
|
256
256
|
janito/tools/adapters/local/validate_file_syntax/xml_validator.py,sha256=AijlsP_PgNuC8ZbGsC5vOTt3Jur76otQzkd_7qR0QFY,284
|
257
257
|
janito/tools/adapters/local/validate_file_syntax/yaml_validator.py,sha256=TgyI0HRL6ug_gBcWEm5TGJJuA4E34ZXcIzMpAbv3oJs,155
|
258
|
-
janito-2.
|
259
|
-
janito-2.
|
260
|
-
janito-2.
|
261
|
-
janito-2.
|
262
|
-
janito-2.
|
263
|
-
janito-2.
|
258
|
+
janito-2.33.0.dist-info/licenses/LICENSE,sha256=dXV4fOF2ZErugtN8l_Nrj5tsRTYgtjE3cgiya0UfBio,11356
|
259
|
+
janito-2.33.0.dist-info/METADATA,sha256=VvNpRDQ9_KEcxzU3YvUh3dXFr6ja6XSEaxpBJ_ydEIU,2313
|
260
|
+
janito-2.33.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
261
|
+
janito-2.33.0.dist-info/entry_points.txt,sha256=wIo5zZxbmu4fC-ZMrsKD0T0vq7IqkOOLYhrqRGypkx4,48
|
262
|
+
janito-2.33.0.dist-info/top_level.txt,sha256=m0NaVCq0-ivxbazE2-ND0EA9Hmuijj_OGkmCbnBcCig,7
|
263
|
+
janito-2.33.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|