gemcode 0.3.25__tar.gz → 0.3.27__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.
- {gemcode-0.3.25/src/gemcode.egg-info → gemcode-0.3.27}/PKG-INFO +1 -1
- {gemcode-0.3.25 → gemcode-0.3.27}/pyproject.toml +1 -1
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/tui/scrollback.py +107 -43
- {gemcode-0.3.25 → gemcode-0.3.27/src/gemcode.egg-info}/PKG-INFO +1 -1
- {gemcode-0.3.25 → gemcode-0.3.27}/LICENSE +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/MANIFEST.in +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/README.md +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/setup.cfg +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/__init__.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/__main__.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/agent.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/audit.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/autocompact.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/callbacks.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/capability_routing.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/cli.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/compaction.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/computer_use/__init__.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/computer_use/browser_computer.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/config.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/context_budget.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/context_warning.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/credentials.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/hitl_session.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/interactions.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/invoke.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/kairos_daemon.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/limits.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/live_audio_engine.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/logging_config.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/mcp_loader.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/memory/__init__.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/memory/embedding_memory_service.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/memory/file_memory_service.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/modality_tools.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/model_errors.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/model_routing.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/paths.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/permissions.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/plugins/__init__.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/plugins/terminal_hooks_plugin.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/plugins/tool_recovery_plugin.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/prompt_suggestions.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/query/__init__.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/query/config.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/query/deps.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/query/engine.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/query/stop_hooks.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/query/token_budget.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/query/transitions.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/repl_commands.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/repl_slash.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/session_runtime.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/slash_commands.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/thinking.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/tool_prompt_manifest.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/tool_registry.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/tools/__init__.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/tools/bash.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/tools/browser.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/tools/edit.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/tools/filesystem.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/tools/search.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/tools/shell.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/tools/shell_gate.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/tools/subtask.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/tools/think.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/tools/todo.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/tools/web.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/tools_inspector.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/trust.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/tui/input_handler.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/tui/spinner.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/tui/welcome_banner.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/tui/welcome_rich.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/version.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/vertex.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/web/__init__.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/web/claude_sse_adapter.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/web/terminal_repl.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode/workspace_hints.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode.egg-info/SOURCES.txt +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode.egg-info/dependency_links.txt +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode.egg-info/entry_points.txt +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode.egg-info/requires.txt +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/src/gemcode.egg-info/top_level.txt +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_agent_instruction.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_autocompact.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_capability_routing.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_claude_web_adapter_sse.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_cli_init.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_computer_use_permissions.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_context_budget.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_context_warning.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_credentials.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_interactive_permission_ask.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_kairos_scheduler.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_modality_tools.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_model_error_retry.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_model_errors.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_model_routing.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_paths.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_permissions.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_prompt_suggestions.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_repl_commands.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_repl_slash.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_slash_commands.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_thinking_config.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_token_budget.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_tool_context_circulation.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_tools.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_tools_inspector.py +0 -0
- {gemcode-0.3.25 → gemcode-0.3.27}/tests/test_workspace_hints.py +0 -0
|
@@ -140,6 +140,43 @@ class _Ansi:
|
|
|
140
140
|
return self.esc("38;5;69")
|
|
141
141
|
|
|
142
142
|
|
|
143
|
+
async def _read_permission_char(loop) -> bool:
|
|
144
|
+
"""
|
|
145
|
+
Read a single character from stdin without requiring Enter.
|
|
146
|
+
|
|
147
|
+
Uses cbreak mode (Unix) so the user just presses 'y' — no Enter needed.
|
|
148
|
+
This sidesteps the prompt_toolkit raw-mode conflict entirely: after
|
|
149
|
+
prompt_async() returns, the terminal may still behave as if Enter sends
|
|
150
|
+
\\r instead of \\n, causing input()/readline() to block forever.
|
|
151
|
+
cbreak mode + read(1) works regardless of the terminal's current line-
|
|
152
|
+
discipline state.
|
|
153
|
+
|
|
154
|
+
Falls back to readline() on Windows or non-TTY (CI, piped input).
|
|
155
|
+
"""
|
|
156
|
+
# ── Unix / macOS: cbreak + read(1) ───────────────────────────────────────
|
|
157
|
+
try:
|
|
158
|
+
import termios
|
|
159
|
+
import tty
|
|
160
|
+
|
|
161
|
+
fd = sys.stdin.fileno()
|
|
162
|
+
old_settings = termios.tcgetattr(fd)
|
|
163
|
+
try:
|
|
164
|
+
tty.setcbreak(fd) # single-char, no echo, signals still work
|
|
165
|
+
ch = await loop.run_in_executor(None, lambda: sys.stdin.read(1))
|
|
166
|
+
finally:
|
|
167
|
+
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
|
|
168
|
+
return ch.lower() == "y"
|
|
169
|
+
except Exception:
|
|
170
|
+
pass
|
|
171
|
+
|
|
172
|
+
# ── Fallback: readline in thread (Windows, non-TTY, termios error) ───────
|
|
173
|
+
try:
|
|
174
|
+
raw = await loop.run_in_executor(None, sys.stdin.readline)
|
|
175
|
+
return (raw or "").replace("\r", "").replace("\n", "").strip().lower() in ("y", "yes")
|
|
176
|
+
except EOFError:
|
|
177
|
+
return False
|
|
178
|
+
|
|
179
|
+
|
|
143
180
|
def _term_width(default: int = 100) -> int:
|
|
144
181
|
try:
|
|
145
182
|
import shutil
|
|
@@ -215,15 +252,26 @@ async def run_gemcode_scrollback_tui(
|
|
|
215
252
|
REQUEST_CONFIRMATION_FC = _ADK_REQUEST_CONFIRMATION
|
|
216
253
|
|
|
217
254
|
def _get_confirmation_fcs(events: list) -> list[types.FunctionCall]:
|
|
218
|
-
|
|
219
|
-
|
|
255
|
+
"""Return confirmation FCs from the LAST event that contains them.
|
|
256
|
+
|
|
257
|
+
The ADK runner expects function responses only for the function calls
|
|
258
|
+
in the most recent event. Sending responses for FCs from earlier events
|
|
259
|
+
in the same batch raises:
|
|
260
|
+
ValueError: Last response event should only contain the responses for
|
|
261
|
+
the function calls in the same function call event.
|
|
262
|
+
So we scan backwards and return only the first (= last) match.
|
|
263
|
+
"""
|
|
264
|
+
for ev in reversed(events):
|
|
220
265
|
try:
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
266
|
+
fcs = [
|
|
267
|
+
fc for fc in (ev.get_function_calls() or [])
|
|
268
|
+
if getattr(fc, "name", None) == _ADK_REQUEST_CONFIRMATION
|
|
269
|
+
]
|
|
270
|
+
if fcs:
|
|
271
|
+
return fcs
|
|
224
272
|
except Exception:
|
|
225
273
|
continue
|
|
226
|
-
return
|
|
274
|
+
return []
|
|
227
275
|
|
|
228
276
|
def _extract_tool_and_hint(fc: types.FunctionCall) -> tuple[str, str]:
|
|
229
277
|
tool_name = "unknown_tool"
|
|
@@ -490,26 +538,40 @@ async def run_gemcode_scrollback_tui(
|
|
|
490
538
|
# as different phases of the turn complete.
|
|
491
539
|
_start_anim("Thinking\u2026")
|
|
492
540
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
if not getattr(ev, "author", None) or ev.author == "user":
|
|
501
|
-
continue
|
|
502
|
-
for part in ev.content.parts:
|
|
503
|
-
delta = getattr(part, "text", None)
|
|
504
|
-
if not delta:
|
|
541
|
+
try:
|
|
542
|
+
async for ev in runner.run_async(**kwargs):
|
|
543
|
+
events.append(ev)
|
|
544
|
+
_render_tool_calls(ev)
|
|
545
|
+
_render_tool_results(ev)
|
|
546
|
+
try:
|
|
547
|
+
if not ev.content or not ev.content.parts:
|
|
505
548
|
continue
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
549
|
+
if not getattr(ev, "author", None) or ev.author == "user":
|
|
550
|
+
continue
|
|
551
|
+
for part in ev.content.parts:
|
|
552
|
+
delta = getattr(part, "text", None)
|
|
553
|
+
if not delta:
|
|
554
|
+
continue
|
|
555
|
+
assistant_wrote_text = True
|
|
556
|
+
if getattr(part, "thought", None):
|
|
557
|
+
buffered_thought.append(delta)
|
|
558
|
+
else:
|
|
559
|
+
buffered_final.append(delta)
|
|
560
|
+
except Exception:
|
|
561
|
+
continue
|
|
562
|
+
except Exception as _turn_err:
|
|
563
|
+
# Catch runner errors (e.g. ADK ValueError from mismatched function
|
|
564
|
+
# response IDs) so a single bad turn doesn't crash the whole TUI.
|
|
565
|
+
_stop_anim()
|
|
566
|
+
print(
|
|
567
|
+
f"\n {ansi.blue_warn}[gemcode] turn error: "
|
|
568
|
+
f"{type(_turn_err).__name__}: {_turn_err}{ansi.reset}"
|
|
569
|
+
)
|
|
570
|
+
print(
|
|
571
|
+
f" {ansi.dim}The agent encountered an internal error on this turn. "
|
|
572
|
+
f"Please send your message again.{ansi.reset}\n"
|
|
573
|
+
)
|
|
574
|
+
break
|
|
513
575
|
|
|
514
576
|
_stop_anim() # ensure spinner is gone before printing the response
|
|
515
577
|
|
|
@@ -581,27 +643,29 @@ async def run_gemcode_scrollback_tui(
|
|
|
581
643
|
f"for {ansi.bold}{tool_name}{ansi.reset}."
|
|
582
644
|
)
|
|
583
645
|
sys.stdout.flush()
|
|
584
|
-
#
|
|
585
|
-
#
|
|
586
|
-
#
|
|
587
|
-
#
|
|
646
|
+
# The core issue: prompt_toolkit puts the terminal in raw mode
|
|
647
|
+
# while reading user input. After prompt_async() returns, the
|
|
648
|
+
# terminal may still be in (or close to) raw mode. In raw mode
|
|
649
|
+
# pressing Enter sends \r instead of \n. Both input() and
|
|
650
|
+
# readline() wait for \n — so they block forever.
|
|
651
|
+
#
|
|
652
|
+
# Fix: read a SINGLE CHARACTER using cbreak mode (no Enter needed).
|
|
653
|
+
# - tty.setcbreak() disables line-buffering + echo but keeps
|
|
654
|
+
# signal keys (Ctrl+C etc.) working.
|
|
655
|
+
# - read(1) returns immediately after any key is pressed.
|
|
656
|
+
# - The user only needs to press "y" — no Enter required.
|
|
657
|
+
# - On Windows (no termios) we fall back to readline().
|
|
588
658
|
prompt_str = (
|
|
589
659
|
f" ⎿ Allow? "
|
|
590
|
-
f"[{ansi.blue_ok}y{ansi.reset} = yes"
|
|
591
|
-
f"
|
|
660
|
+
f"[{ansi.blue_ok}y{ansi.reset} = yes "
|
|
661
|
+
f"{ansi.dim}any other key = no{ansi.reset}] "
|
|
592
662
|
)
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
# Strip \r and \n — handles raw-mode terminals where pressing
|
|
600
|
-
# Enter sends \r (shown as ^M) instead of \n.
|
|
601
|
-
ans = (raw or "").replace("\r", "").replace("\n", "").strip().lower()
|
|
602
|
-
except EOFError:
|
|
603
|
-
ans = ""
|
|
604
|
-
ok = ans in ("y", "yes")
|
|
663
|
+
sys.stdout.write(prompt_str)
|
|
664
|
+
sys.stdout.flush()
|
|
665
|
+
ok = await _read_permission_char(asyncio.get_running_loop())
|
|
666
|
+
# Echo the answer and move to next line
|
|
667
|
+
sys.stdout.write(("y" if ok else "n") + "\n")
|
|
668
|
+
sys.stdout.flush()
|
|
605
669
|
|
|
606
670
|
# Explicit visual feedback — user knows their answer was received.
|
|
607
671
|
if ok:
|
|
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
|
|
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
|