dbagent-cli 0.9.8__tar.gz → 0.9.9__tar.gz
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.
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/PKG-INFO +1 -1
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/__init__.py +1 -1
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/cli.py +12 -10
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/schema/ground_truth.py +5 -3
- dbagent_cli-0.9.9/dbagent/ui/live_streamer.py +117 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent_cli.egg-info/PKG-INFO +1 -1
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent_cli.egg-info/SOURCES.txt +1 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/pyproject.toml +1 -1
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/README.md +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/agent/confidence.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/agent/conversation.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/agent/doctor.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/agent/error_classifier.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/agent/generator.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/agent/pipeline.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/agent/recommender.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/agent/validator.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/config.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/connectors/base.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/connectors/factory.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/connectors/mongo.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/connectors/relational.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/execution/__init__.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/execution/cache.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/execution/changeset.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/execution/cost.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/execution/recovery.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/execution/sandbox.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/execution/streaming.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/llm/anthropic_provider.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/llm/auto_setup.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/llm/base.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/llm/deepseek_provider.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/llm/factory.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/llm/gemini_provider.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/llm/groq_provider.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/llm/mistral_provider.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/llm/mock_provider.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/llm/ollama_provider.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/llm/openai_provider.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/llm/openrouter_provider.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/schema/cache.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/schema/formatter.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/schema/graph.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/schema/models.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/schema/retriever.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/schema/selector.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/security/__init__.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/security/audit.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/security/environment.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/security/masking.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/security/modes.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/security/rbac.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/semantic/__init__.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/semantic/dictionary.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/semantic/knowledge.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/semantic/templates.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/sql/__init__.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/sql/optimizer.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/sql/validator.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/ui/chat_screen.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/ui/console.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/ui/grid_window.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/ui/viewer.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent/ui/visualizer.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent_cli.egg-info/dependency_links.txt +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent_cli.egg-info/entry_points.txt +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent_cli.egg-info/requires.txt +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/dbagent_cli.egg-info/top_level.txt +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/setup.cfg +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/tests/test_cache.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/tests/test_cli.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/tests/test_connectors.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/tests/test_direct_sql.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/tests/test_doctor_and_viz.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/tests/test_generator.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/tests/test_graph.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/tests/test_knowledge.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/tests/test_providers.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/tests/test_recovery.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/tests/test_safety.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/tests/test_sandbox.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/tests/test_schema.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/tests/test_security.py +0 -0
- {dbagent_cli-0.9.8 → dbagent_cli-0.9.9}/tests/test_semantic.py +0 -0
|
@@ -530,6 +530,8 @@ def ask_command(
|
|
|
530
530
|
choice_callback=_choice_callback,
|
|
531
531
|
)
|
|
532
532
|
|
|
533
|
+
from dbagent.ui.live_streamer import LiveMarkdownStreamer
|
|
534
|
+
streamer = LiveMarkdownStreamer(console)
|
|
533
535
|
spinner = DynamicSpinner.for_prompt(prompt, model_name=model or getattr(llm, "default_model", None), connector=connector)
|
|
534
536
|
stream_started = [False]
|
|
535
537
|
|
|
@@ -541,15 +543,14 @@ def ask_command(
|
|
|
541
543
|
active_model = model or getattr(llm, "default_model", None)
|
|
542
544
|
model_display = f" ({active_model})" if active_model else ""
|
|
543
545
|
console.print(f"\n[bold cyan]● DB-Agent{model_display}[/bold cyan] [dim]{timing_str}[/dim]")
|
|
544
|
-
|
|
545
|
-
sys.stdout.write(chunk)
|
|
546
|
-
sys.stdout.flush()
|
|
547
|
-
except Exception:
|
|
548
|
-
pass
|
|
546
|
+
streamer.feed(chunk)
|
|
549
547
|
|
|
550
548
|
with spinner:
|
|
551
549
|
result = pipeline.run(user_prompt=prompt, force=force, bypass_cache=no_cache, stream_callback=_query_stream_callback)
|
|
552
550
|
|
|
551
|
+
if stream_started[0]:
|
|
552
|
+
streamer.finalize()
|
|
553
|
+
|
|
553
554
|
_display_pipeline_result(result, show_sql=True)
|
|
554
555
|
connector.close()
|
|
555
556
|
|
|
@@ -1119,6 +1120,8 @@ def chat_command(
|
|
|
1119
1120
|
pipeline.mode = current_mode
|
|
1120
1121
|
pipeline.auto_execute = auto_execute
|
|
1121
1122
|
|
|
1123
|
+
from dbagent.ui.live_streamer import LiveMarkdownStreamer
|
|
1124
|
+
streamer = LiveMarkdownStreamer(console)
|
|
1122
1125
|
spinner = DynamicSpinner.for_prompt(user_input, model_name=model or getattr(llm, "default_model", None), connector=connector)
|
|
1123
1126
|
stream_started = [False]
|
|
1124
1127
|
|
|
@@ -1130,15 +1133,14 @@ def chat_command(
|
|
|
1130
1133
|
active_model = model or getattr(llm, "default_model", None)
|
|
1131
1134
|
model_display = f" ({active_model})" if active_model else ""
|
|
1132
1135
|
console.print(f"\n[bold cyan]● DB-Agent{model_display}[/bold cyan] [dim]{timing_str}[/dim]")
|
|
1133
|
-
|
|
1134
|
-
sys.stdout.write(chunk)
|
|
1135
|
-
sys.stdout.flush()
|
|
1136
|
-
except Exception:
|
|
1137
|
-
pass
|
|
1136
|
+
streamer.feed(chunk)
|
|
1138
1137
|
|
|
1139
1138
|
with spinner:
|
|
1140
1139
|
result = pipeline.run(user_prompt=user_input, stream_callback=_live_stream_callback)
|
|
1141
1140
|
|
|
1141
|
+
if stream_started[0]:
|
|
1142
|
+
streamer.finalize()
|
|
1143
|
+
|
|
1142
1144
|
render_assistant_turn(
|
|
1143
1145
|
result,
|
|
1144
1146
|
show_sql=True,
|
|
@@ -85,17 +85,19 @@ class SchemaGroundTruthValidator:
|
|
|
85
85
|
if fk_desc not in result.verified_fks:
|
|
86
86
|
result.verified_fks.append(fk_desc)
|
|
87
87
|
|
|
88
|
-
# 2. Extract and validate SQL queries against physical schema
|
|
89
|
-
sql_blocks = re.findall(r'```(?:sql)
|
|
88
|
+
# 2. Extract and validate only actual SQL queries against physical schema
|
|
89
|
+
sql_blocks = re.findall(r'```(?:sql)\s*([\s\S]*?)\s*```', response_text, re.IGNORECASE)
|
|
90
90
|
result.sql_queries_checked = len(sql_blocks)
|
|
91
91
|
sql_referenced_tables = set()
|
|
92
|
+
sql_ignore_keywords = {"via", "lateral", "on", "using", "where", "select", "table", "as", "and", "or", "values"}
|
|
92
93
|
|
|
93
94
|
for block in sql_blocks:
|
|
94
95
|
# Extract FROM and JOIN table references
|
|
95
96
|
matches = re.findall(r'\b(?:FROM|JOIN)\s+([a-zA-Z0-9_]+)', block, re.IGNORECASE)
|
|
96
97
|
for m in matches:
|
|
97
98
|
m_clean = m.strip().lower()
|
|
98
|
-
|
|
99
|
+
if m_clean not in sql_ignore_keywords:
|
|
100
|
+
sql_referenced_tables.add(m_clean)
|
|
99
101
|
|
|
100
102
|
for sql_tbl in sql_referenced_tables:
|
|
101
103
|
if sql_tbl in schema_table_map:
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Real-Time Line-Buffered Rich Markdown Streamer for DB-Agent CLI.
|
|
3
|
+
Renders markdown headers, formatted diagram panels, syntax-highlighted SQL blocks,
|
|
4
|
+
and sleek bulleted lists dynamically as tokens stream in from LLM providers.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
import sys
|
|
9
|
+
from typing import List, Optional
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
from rich.panel import Panel
|
|
12
|
+
from rich.syntax import Syntax
|
|
13
|
+
from rich.rule import Rule
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class LiveMarkdownStreamer:
|
|
17
|
+
"""
|
|
18
|
+
Line-buffered dynamic terminal renderer for streaming LLM responses.
|
|
19
|
+
Renders syntax-highlighted code blocks, bordered ASCII diagrams,
|
|
20
|
+
styled markdown headers, and clean bullet lists live as tokens arrive.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self, console: Optional[Console] = None):
|
|
24
|
+
self.console = console or Console()
|
|
25
|
+
self.buffer = ""
|
|
26
|
+
self.in_code_block = False
|
|
27
|
+
self.code_lang = ""
|
|
28
|
+
self.code_lines: List[str] = []
|
|
29
|
+
self._has_started = False
|
|
30
|
+
|
|
31
|
+
def feed(self, chunk: str) -> None:
|
|
32
|
+
"""Feed a token chunk from the LLM stream."""
|
|
33
|
+
self._has_started = True
|
|
34
|
+
self.buffer += chunk
|
|
35
|
+
while "\n" in self.buffer:
|
|
36
|
+
line, self.buffer = self.buffer.split("\n", 1)
|
|
37
|
+
self._process_line(line)
|
|
38
|
+
|
|
39
|
+
def _process_line(self, line: str) -> None:
|
|
40
|
+
line_strip = line.strip()
|
|
41
|
+
|
|
42
|
+
# Code block fence (```sql, ```text, ```)
|
|
43
|
+
if line_strip.startswith("```"):
|
|
44
|
+
if self.in_code_block:
|
|
45
|
+
# Close code block
|
|
46
|
+
self.in_code_block = False
|
|
47
|
+
code_text = "\n".join(self.code_lines)
|
|
48
|
+
lang = self.code_lang or "sql"
|
|
49
|
+
if lang in ["sql", "postgres", "postgresql", "mysql", "sqlite", "tsql"]:
|
|
50
|
+
syntax = Syntax(code_text, "sql", theme="monokai", line_numbers=False)
|
|
51
|
+
self.console.print(Panel(syntax, title="[dim]SQL Statement[/dim]", title_align="left", border_style="dim", padding=(0, 1)))
|
|
52
|
+
else:
|
|
53
|
+
syntax = Syntax(code_text, "text", theme="monokai", line_numbers=False)
|
|
54
|
+
self.console.print(Panel(syntax, title="[dim]Architecture Flow Diagram[/dim]", title_align="left", border_style="cyan", padding=(0, 1)))
|
|
55
|
+
self.code_lines = []
|
|
56
|
+
self.code_lang = ""
|
|
57
|
+
else:
|
|
58
|
+
# Open code block
|
|
59
|
+
self.in_code_block = True
|
|
60
|
+
self.code_lang = line_strip[3:].strip().lower()
|
|
61
|
+
self.code_lines = []
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
if self.in_code_block:
|
|
65
|
+
self.code_lines.append(line)
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
# Markdown Section Headers (#, ##, ###)
|
|
69
|
+
if line_strip.startswith("#"):
|
|
70
|
+
header_text = line_strip.lstrip("#").strip()
|
|
71
|
+
self.console.print("")
|
|
72
|
+
self.console.print(f"[bold cyan]{header_text}[/bold cyan]")
|
|
73
|
+
return
|
|
74
|
+
|
|
75
|
+
# Markdown Horizontal Rules (---, ***, ___)
|
|
76
|
+
if line_strip in ["---", "***", "___"]:
|
|
77
|
+
self.console.print(Rule(style="dim cyan"))
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
# Markdown Bullet Points (- , * , • )
|
|
81
|
+
if line_strip.startswith(("- ", "* ", "• ")):
|
|
82
|
+
content = line_strip[2:].strip()
|
|
83
|
+
styled = re.sub(r'\*\*(.*?)\*\*', r'[bold cyan]\1[/bold cyan]', content)
|
|
84
|
+
styled = re.sub(r'`(.*?)`', r'[bold white]\1[/bold white]', styled)
|
|
85
|
+
self.console.print(f" [dim]•[/dim] {styled}")
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
# Numbered List items (1. , 2. )
|
|
89
|
+
num_match = re.match(r'^(\d+[\.\)])\s+(.*)', line_strip)
|
|
90
|
+
if num_match:
|
|
91
|
+
num_prefix, content = num_match.groups()
|
|
92
|
+
styled = re.sub(r'\*\*(.*?)\*\*', r'[bold cyan]\1[/bold cyan]', content)
|
|
93
|
+
styled = re.sub(r'`(.*?)`', r'[bold white]\1[/bold white]', styled)
|
|
94
|
+
self.console.print(f" [bold yellow]{num_prefix}[/bold yellow] {styled}")
|
|
95
|
+
return
|
|
96
|
+
|
|
97
|
+
# Blank Line
|
|
98
|
+
if not line_strip:
|
|
99
|
+
self.console.print("")
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
# Regular Markdown paragraph text with bold and inline code styling
|
|
103
|
+
styled_line = re.sub(r'\*\*(.*?)\*\*', r'[bold cyan]\1[/bold cyan]', line)
|
|
104
|
+
styled_line = re.sub(r'`(.*?)`', r'[bold white]\1[/bold white]', styled_line)
|
|
105
|
+
self.console.print(styled_line)
|
|
106
|
+
|
|
107
|
+
def finalize(self) -> None:
|
|
108
|
+
"""Flush remaining buffer and close any dangling open blocks."""
|
|
109
|
+
if self.buffer.strip():
|
|
110
|
+
self._process_line(self.buffer)
|
|
111
|
+
if self.in_code_block and self.code_lines:
|
|
112
|
+
code_text = "\n".join(self.code_lines)
|
|
113
|
+
syntax = Syntax(code_text, self.code_lang or "sql", theme="monokai", line_numbers=False)
|
|
114
|
+
self.console.print(Panel(syntax, border_style="dim", padding=(0, 1)))
|
|
115
|
+
self.buffer = ""
|
|
116
|
+
self.code_lines = []
|
|
117
|
+
self.in_code_block = False
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "dbagent-cli"
|
|
7
|
-
version = "0.9.
|
|
7
|
+
version = "0.9.9"
|
|
8
8
|
description = "Enterprise AI Database Copilot, Schema Graph Introspector & Autonomous Recovery Agent (CLI)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|