webbee 0.2.1__tar.gz → 0.2.3__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.
- {webbee-0.2.1 → webbee-0.2.3}/CHANGELOG.md +17 -0
- {webbee-0.2.1 → webbee-0.2.3}/PKG-INFO +1 -1
- {webbee-0.2.1 → webbee-0.2.3}/pyproject.toml +1 -1
- webbee-0.2.3/src/webbee/__init__.py +1 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/render.py +21 -2
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/session.py +3 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/tui.py +20 -11
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_marathon.py +11 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_tui.py +44 -14
- webbee-0.2.1/src/webbee/__init__.py +0 -1
- {webbee-0.2.1 → webbee-0.2.3}/.github/workflows/publish.yml +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/.gitignore +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/LICENSE +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/README.md +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/install.sh +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/account.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/banner_art.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/cli.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/clipboard.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/commands.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/config.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/details.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/events.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/frames.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/intel/__init__.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/intel/chunker.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/intel/embed.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/intel/graph.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/intel/indexer.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/intel/models.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/intel/query.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/intel/service.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/intel/store.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/intel/vectors.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/intel/watch.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/output_pane.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/repl.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/repo.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/sessions.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/stream.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/tools.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/src/webbee/update.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/__init__.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_account.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_cli.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_clipboard.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_commands.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_config.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_cpc_contract_stable.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_details.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_events.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_freeze_fix.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_intel_chunker.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_intel_embed.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_intel_graph.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_intel_indexer.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_intel_no_numpy.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_intel_query.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_intel_service.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_intel_store.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_intel_vectors.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_intel_watch.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_packaging.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_render.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_repl.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_repo.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_session.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_sessions.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_stream.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_tools.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_tui_hardening.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_update.py +0 -0
- {webbee-0.2.1 → webbee-0.2.3}/tests/test_version.py +0 -0
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.3
|
|
4
|
+
|
|
5
|
+
- **Readable big numbers.** Token and credit counts in the toolbar now scale
|
|
6
|
+
(`1.5M tok`, `2M credits`) instead of an awkward `1500.0k`.
|
|
7
|
+
- **Esc really stops a turn now.** Previously only Ctrl-C worked; Esc now cancels
|
|
8
|
+
the running turn too, matching the "Esc/Ctrl-C to stop" hint.
|
|
9
|
+
- **The `❯` prompt takes your mode's colour** — cyan (default), purple (plan),
|
|
10
|
+
yellow (autopilot) — so the current mode is obvious from the input line.
|
|
11
|
+
|
|
12
|
+
## 0.2.2
|
|
13
|
+
|
|
14
|
+
- **See Webbee's reasoning as a distinct 💭 block.** Before each step, Webbee now
|
|
15
|
+
shows a genuine line of thinking — what it's weighing and why — rendered as its
|
|
16
|
+
own 💭 block, separate from status lines. Works on every model.
|
|
17
|
+
- **SSH steps are visible.** When Webbee runs a command on one of your connected
|
|
18
|
+
servers, the terminal now shows that step (command + result), like any other.
|
|
19
|
+
|
|
3
20
|
## 0.2.1
|
|
4
21
|
|
|
5
22
|
- **Fixes a marathon that could hang on "working" and quietly burn credits.** A
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.3"
|
|
@@ -27,8 +27,20 @@ _ACCENT = "cyan" # interactive chrome ONLY — live caret / mode / panel ur
|
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
def _fmt_tokens(n: int) -> str:
|
|
30
|
-
"""Compact
|
|
31
|
-
|
|
30
|
+
"""Compact count for the live toolbar: 900 -> '900', 2_100 -> '2.1k',
|
|
31
|
+
1_500_000 -> '1.5M', 2_000_000 -> '2M', 3_200_000_000 -> '3.2B'. Used for
|
|
32
|
+
both token counts and (integer) credits so big numbers stay readable."""
|
|
33
|
+
try:
|
|
34
|
+
n = int(n or 0)
|
|
35
|
+
except (TypeError, ValueError):
|
|
36
|
+
return "0"
|
|
37
|
+
a = abs(n)
|
|
38
|
+
if a < 1000:
|
|
39
|
+
return str(n)
|
|
40
|
+
for div, suf in ((1_000_000_000, "B"), (1_000_000, "M"), (1000, "k")):
|
|
41
|
+
if a >= div:
|
|
42
|
+
return f"{n / div:.1f}".rstrip("0").rstrip(".") + suf
|
|
43
|
+
return str(n)
|
|
32
44
|
|
|
33
45
|
|
|
34
46
|
def _salient_arg(args: dict) -> str:
|
|
@@ -325,6 +337,13 @@ class RichSink:
|
|
|
325
337
|
self.console.print(Text(" " + text, style="dim italic"))
|
|
326
338
|
self._nudge()
|
|
327
339
|
|
|
340
|
+
def thinking(self, text: str) -> None:
|
|
341
|
+
# System-driven reasoning as a distinct 💭 block — visually apart from the
|
|
342
|
+
# dim `progress` line (which stays reserved for status like low-balance).
|
|
343
|
+
if text:
|
|
344
|
+
self.console.print(Text(" 💭 " + text, style="italic medium_purple3"))
|
|
345
|
+
self._nudge()
|
|
346
|
+
|
|
328
347
|
def plan_blocked(self, tool: str) -> None:
|
|
329
348
|
"""Plan mode auto-declines writes/destructive. Tell the user WHY and how
|
|
330
349
|
to allow it (Shift+Tab). Autopilot and default never reach this."""
|
|
@@ -272,6 +272,9 @@ class AgentSession:
|
|
|
272
272
|
elif ftype == "step_finished": # v2 (Slice-5 T8 dual-emit)
|
|
273
273
|
handle_step_finished(frame, sink, finished, step_labels, self.steps, local_ids)
|
|
274
274
|
|
|
275
|
+
elif ftype == "thinking": # system-driven reasoning -> the 💭 block
|
|
276
|
+
(getattr(sink, "thinking", None) or sink.progress)(_progress_text(frame))
|
|
277
|
+
|
|
275
278
|
elif ftype == "progress": # P2 — dual-reads llm_text (v2) / text (legacy)
|
|
276
279
|
sink.progress(_progress_text(frame))
|
|
277
280
|
|
|
@@ -44,15 +44,19 @@ def build_toolbar(mode: str, tokens: int, credits: int, *, busy: bool = False,
|
|
|
44
44
|
return [("class:tb.dim", " mode: "),
|
|
45
45
|
(f"class:tb.mode.{mode}", mode),
|
|
46
46
|
("class:tb.dim",
|
|
47
|
-
f" · {_fmt_tokens(tokens)} tok · {credits} credits · Shift + TAB: switch mode")]
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def _escape_action(sel: dict, is_busy, stop_turn, event) -> None:
|
|
51
|
-
"""Esc key binding (P5g). While a turn is running,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if is_busy()
|
|
55
|
-
|
|
47
|
+
f" · {_fmt_tokens(tokens)} tok · {_fmt_tokens(credits)} credits · Shift + TAB: switch mode")]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _escape_action(sel: dict, turn: dict, is_busy, stop_turn, event) -> None:
|
|
51
|
+
"""Esc key binding (P5g). While a turn is running, STOP it — cancel the LOCAL
|
|
52
|
+
turn task (what actually tears the turn down, same as Ctrl-C) AND ask the
|
|
53
|
+
server to stop (so it stops spending). While idle, clear the step-selection."""
|
|
54
|
+
if is_busy():
|
|
55
|
+
t = turn.get("task")
|
|
56
|
+
if t is not None and not t.done():
|
|
57
|
+
if stop_turn is not None:
|
|
58
|
+
event.app.create_background_task(stop_turn())
|
|
59
|
+
t.cancel() # cancel the running turn; dock survives
|
|
56
60
|
return
|
|
57
61
|
sel["i"] = None
|
|
58
62
|
event.app.invalidate()
|
|
@@ -157,7 +161,7 @@ async def run_session(*, pane, on_line, mode_getter, on_cycle, status,
|
|
|
157
161
|
|
|
158
162
|
@kb.add("escape")
|
|
159
163
|
def _step_clear(event):
|
|
160
|
-
_escape_action(sel, is_busy, stop_turn, event)
|
|
164
|
+
_escape_action(sel, turn, is_busy, stop_turn, event)
|
|
161
165
|
|
|
162
166
|
@kb.add("pageup")
|
|
163
167
|
def _pgup(event):
|
|
@@ -190,8 +194,13 @@ async def run_session(*, pane, on_line, mode_getter, on_cycle, status,
|
|
|
190
194
|
rows = sum(max(1, -(-len(ln) // cols)) for ln in text.split("\n"))
|
|
191
195
|
return min(10, max(1, rows))
|
|
192
196
|
|
|
197
|
+
def _prompt_fragments():
|
|
198
|
+
# The ❯ takes the CURRENT mode's colour (same classes the toolbar uses)
|
|
199
|
+
# so the mode is obvious from the input line itself, not just the toolbar.
|
|
200
|
+
return [(f"class:tb.mode.{mode_getter()}", "❯ ")]
|
|
201
|
+
|
|
193
202
|
input_win = Window(
|
|
194
|
-
BufferControl(buffer=buf, input_processors=[BeforeInput(
|
|
203
|
+
BufferControl(buffer=buf, input_processors=[BeforeInput(_prompt_fragments)]),
|
|
195
204
|
height=_input_height, wrap_lines=True)
|
|
196
205
|
toolbar = Window(FormattedTextControl(_toolbar), height=1, always_hide_cursor=True)
|
|
197
206
|
root = HSplit([pane.window, Frame(input_win), toolbar])
|
|
@@ -154,6 +154,7 @@ def _run_marathon_capture_post(monkeypatch, tmp_path, goal="build X", frames=Non
|
|
|
154
154
|
class NoteSink:
|
|
155
155
|
def __init__(self): self.notes = []
|
|
156
156
|
def note(self, msg): self.notes.append(msg)
|
|
157
|
+
def thinking(self, msg): self.thinks = getattr(self, "thinks", []) + [msg]
|
|
157
158
|
def tool_start(self, *a): ...
|
|
158
159
|
def tool_result(self, *a): ...
|
|
159
160
|
def ask_consent(self, *a): return "y"
|
|
@@ -202,6 +203,16 @@ def test_marathon_complete_is_terminal(monkeypatch, tmp_path):
|
|
|
202
203
|
assert result == "goal done"
|
|
203
204
|
|
|
204
205
|
|
|
206
|
+
def test_thinking_frame_renders_as_distinct_channel(monkeypatch, tmp_path):
|
|
207
|
+
# A `thinking` frame routes to the distinct 💭 reasoning channel
|
|
208
|
+
# (sink.thinking), NOT the plain progress/note lines.
|
|
209
|
+
_, _, sink = _run_marathon_capture_post(monkeypatch, tmp_path, frames=[
|
|
210
|
+
{"type": "thinking", "task_id": "OURS", "text": "assessing the repo before editing"},
|
|
211
|
+
{"type": "marathon_complete", "task_id": "OURS", "text": "done"},
|
|
212
|
+
])
|
|
213
|
+
assert getattr(sink, "thinks", []) == ["assessing the repo before editing"]
|
|
214
|
+
|
|
215
|
+
|
|
205
216
|
def test_marathon_paused_is_terminal_and_noted(monkeypatch, tmp_path):
|
|
206
217
|
_, result, sink = _run_marathon_capture_post(monkeypatch, tmp_path, frames=[
|
|
207
218
|
{"type": "marathon_paused", "task_id": "OURS", "reason": "out of credits"},
|
|
@@ -22,13 +22,30 @@ def test_next_mode_unknown_resets():
|
|
|
22
22
|
def test_toolbar_idle_has_mode_tokens_cost_and_hint():
|
|
23
23
|
t = _txt(build_toolbar("plan", 51000, 66))
|
|
24
24
|
assert "plan" in t
|
|
25
|
-
assert "
|
|
25
|
+
assert "51k" in t
|
|
26
26
|
assert "66 credits" in t
|
|
27
27
|
assert "Shift + TAB" in t # spelled in words, no glyph
|
|
28
28
|
assert "⇧⇥" not in t # the ⇧⇥ glyph must NOT appear
|
|
29
29
|
assert not NO_CYRILLIC.search(t)
|
|
30
30
|
|
|
31
31
|
|
|
32
|
+
def test_toolbar_humanizes_millions_and_credits():
|
|
33
|
+
# Big numbers stay readable: tokens + credits both scale to k/M/B.
|
|
34
|
+
t = _txt(build_toolbar("default", 1_500_000, 2_000_000))
|
|
35
|
+
assert "1.5M tok" in t
|
|
36
|
+
assert "2M credits" in t
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_fmt_tokens_scales_k_m_b():
|
|
40
|
+
from webbee.render import _fmt_tokens
|
|
41
|
+
assert _fmt_tokens(900) == "900"
|
|
42
|
+
assert _fmt_tokens(2100) == "2.1k"
|
|
43
|
+
assert _fmt_tokens(51000) == "51k"
|
|
44
|
+
assert _fmt_tokens(1_500_000) == "1.5M"
|
|
45
|
+
assert _fmt_tokens(2_000_000) == "2M"
|
|
46
|
+
assert _fmt_tokens(3_200_000_000) == "3.2B"
|
|
47
|
+
|
|
48
|
+
|
|
32
49
|
def test_toolbar_busy_state_shows_working_dot_and_stop_hint():
|
|
33
50
|
t = _txt(build_toolbar("default", 1200, 0.0143, busy=True,
|
|
34
51
|
current="notes·delete_note", elapsed=4, tools=3))
|
|
@@ -203,7 +220,15 @@ def _run_and_drain(coro):
|
|
|
203
220
|
asyncio.run(coro)
|
|
204
221
|
|
|
205
222
|
|
|
206
|
-
|
|
223
|
+
class _FakeTask:
|
|
224
|
+
def __init__(self): self.cancelled = []
|
|
225
|
+
def done(self): return False
|
|
226
|
+
def cancel(self): self.cancelled.append(1)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def test_escape_while_busy_cancels_turn_and_schedules_server_stop():
|
|
230
|
+
# Esc now REALLY stops a running turn: cancel the local turn task (what tears
|
|
231
|
+
# it down) AND ask the server to stop — matching the "Esc/Ctrl-C" hint.
|
|
207
232
|
from webbee.tui import _escape_action
|
|
208
233
|
|
|
209
234
|
stop_calls = []
|
|
@@ -213,13 +238,15 @@ def test_escape_while_busy_schedules_server_stop_and_leaves_selection():
|
|
|
213
238
|
|
|
214
239
|
event = _FakeEvent()
|
|
215
240
|
sel = {"i": 2}
|
|
216
|
-
|
|
241
|
+
task = _FakeTask()
|
|
242
|
+
turn = {"task": task}
|
|
243
|
+
_escape_action(sel, turn, lambda: True, stop_turn, event)
|
|
217
244
|
|
|
245
|
+
assert task.cancelled == [1] # the local turn is actually cancelled
|
|
218
246
|
assert len(event.app.background_tasks) == 1
|
|
219
247
|
_run_and_drain(event.app.background_tasks[0])
|
|
220
|
-
assert stop_calls == [1]
|
|
221
|
-
#
|
|
222
|
-
# or invalidate the toolbar (that's the idle-path job, tested below).
|
|
248
|
+
assert stop_calls == [1] # AND the server is asked to stop
|
|
249
|
+
# Busy-path stops the turn — it does NOT also clear the step-selection.
|
|
223
250
|
assert sel["i"] == 2
|
|
224
251
|
assert event.app.invalidated is False
|
|
225
252
|
|
|
@@ -234,7 +261,8 @@ def test_escape_while_idle_clears_step_selection_unchanged():
|
|
|
234
261
|
|
|
235
262
|
event = _FakeEvent()
|
|
236
263
|
sel = {"i": 2}
|
|
237
|
-
|
|
264
|
+
turn = {"task": None}
|
|
265
|
+
_escape_action(sel, turn, lambda: False, stop_turn, event)
|
|
238
266
|
|
|
239
267
|
assert sel["i"] is None
|
|
240
268
|
assert event.app.invalidated is True
|
|
@@ -242,18 +270,20 @@ def test_escape_while_idle_clears_step_selection_unchanged():
|
|
|
242
270
|
assert stop_calls == []
|
|
243
271
|
|
|
244
272
|
|
|
245
|
-
def
|
|
246
|
-
# stop_turn=None (e.g. no repl wiring) must never crash Esc — it
|
|
247
|
-
#
|
|
273
|
+
def test_escape_while_busy_with_no_stop_turn_still_cancels_locally():
|
|
274
|
+
# stop_turn=None (e.g. no repl wiring) must never crash Esc — it still cancels
|
|
275
|
+
# the local turn task, so Esc reliably stops even without the server hook.
|
|
248
276
|
from webbee.tui import _escape_action
|
|
249
277
|
|
|
250
278
|
event = _FakeEvent()
|
|
251
279
|
sel = {"i": 1}
|
|
252
|
-
|
|
280
|
+
task = _FakeTask()
|
|
281
|
+
turn = {"task": task}
|
|
282
|
+
_escape_action(sel, turn, lambda: True, None, event)
|
|
253
283
|
|
|
254
|
-
assert
|
|
255
|
-
assert event.app.
|
|
256
|
-
assert
|
|
284
|
+
assert task.cancelled == [1] # local teardown happens regardless
|
|
285
|
+
assert event.app.background_tasks == [] # no stop_turn -> nothing scheduled
|
|
286
|
+
assert sel["i"] == 1 # busy-path leaves the selection
|
|
257
287
|
|
|
258
288
|
|
|
259
289
|
def test_interrupt_while_busy_cancels_local_task_and_schedules_server_stop():
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.2.1"
|
|
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
|