deepparallel 0.5.4__tar.gz → 0.5.5__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.
- {deepparallel-0.5.4 → deepparallel-0.5.5}/PKG-INFO +1 -1
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/__init__.py +1 -1
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/cli.py +23 -11
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/cockpit.py +20 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/cockpit_observe.py +9 -2
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel.egg-info/PKG-INFO +1 -1
- {deepparallel-0.5.4 → deepparallel-0.5.5}/pyproject.toml +1 -1
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_cockpit.py +41 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/README.md +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/agent.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/backend.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/branding.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/cockpit_panel.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/cockpit_sim.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/config.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/crowe_id.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/dsml.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/fusion.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/licensing.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/registry.json +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/renderer.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/research/__init__.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/research/conduit.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/research/provider.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/routing.example.json +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/routing.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/serve.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/supply_chain.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/system_prompt.txt +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/tools/__init__.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/tools/codeast.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/tools/edit.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/tools/files.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/tools/mcp.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/tools/registry.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/tools/sandbox.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/tools/search.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/tools/shell.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/tools/vision.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/tools/web.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel/userinput.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel.egg-info/SOURCES.txt +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel.egg-info/dependency_links.txt +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel.egg-info/entry_points.txt +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel.egg-info/requires.txt +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/deepparallel.egg-info/top_level.txt +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/setup.cfg +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_agent.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_backend.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_backend_chat.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_backend_stream.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_branding.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_cli.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_cockpit_panel.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_cockpit_sim.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_config.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_crowe_backend.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_crowe_gateway_backend.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_crowe_id_auth.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_crowe_payment_required.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_dsml.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_fusion.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_issuer_signer.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_licensing.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_renderer.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_research.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_research_provider.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_routing.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_serve.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_spinner_color.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_supply_chain.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_tool_registry.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_tools_codeast.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_tools_edit.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_tools_files.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_tools_mcp.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_tools_sandbox.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_tools_search.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_tools_shell.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_tools_vision.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_tools_web.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_userinput.py +0 -0
- {deepparallel-0.5.4 → deepparallel-0.5.5}/tests/test_userinput_paste.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deepparallel
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.5
|
|
4
4
|
Summary: DeepParallel - a multi-model agentic coding CLI with cross-model Guardian review, served via Crowe Logic.
|
|
5
5
|
Author-email: Michael Crowe <michael@crowelogic.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -17,7 +17,6 @@ apply to chat and run.
|
|
|
17
17
|
from __future__ import annotations
|
|
18
18
|
|
|
19
19
|
import os
|
|
20
|
-
import subprocess
|
|
21
20
|
import sys
|
|
22
21
|
from dataclasses import replace
|
|
23
22
|
from pathlib import Path
|
|
@@ -295,6 +294,13 @@ def _agent_repl(backend: Backend, settings: Settings, renderer: Renderer) -> Non
|
|
|
295
294
|
mode = settings.fusion_mode if settings.fusion_mode in ("reason", "escalate") else "off"
|
|
296
295
|
deep_next = False
|
|
297
296
|
auto = settings.auto_approve
|
|
297
|
+
cockpit = Cockpit(os.path.abspath(os.path.join(".cockpit", "events.jsonl")))
|
|
298
|
+
cockpit_on = cockpit.in_waveterm()
|
|
299
|
+
observer = make_observer(cockpit)
|
|
300
|
+
if cockpit_on:
|
|
301
|
+
cockpit.events_path.parent.mkdir(parents=True, exist_ok=True)
|
|
302
|
+
cockpit.events_path.write_text("")
|
|
303
|
+
cockpit.status("start", "cockpit ready")
|
|
298
304
|
while True:
|
|
299
305
|
bits = ([mode] if mode != "off" else []) + (["auto"] if auto else [])
|
|
300
306
|
tag = f"[{' · '.join(bits)}] " if bits else ""
|
|
@@ -309,7 +315,7 @@ def _agent_repl(backend: Backend, settings: Settings, renderer: Renderer) -> Non
|
|
|
309
315
|
break
|
|
310
316
|
if user_msg == "/help":
|
|
311
317
|
branding.info(
|
|
312
|
-
"/quit · /reset · /info · /tools · /auto · /fast //fuse //escalate //deep · prompt"
|
|
318
|
+
"/quit · /reset · /info · /tools · /auto · /cockpit · /fast //fuse //escalate //deep · prompt"
|
|
313
319
|
)
|
|
314
320
|
continue
|
|
315
321
|
if user_msg in {"/auto", "/yes"}:
|
|
@@ -337,6 +343,17 @@ def _agent_repl(backend: Backend, settings: Settings, renderer: Renderer) -> Non
|
|
|
337
343
|
deep_next = True
|
|
338
344
|
branding.info("next prompt runs as a multi-model deep query")
|
|
339
345
|
continue
|
|
346
|
+
if user_msg == "/cockpit":
|
|
347
|
+
cockpit_on = not cockpit_on
|
|
348
|
+
if cockpit_on:
|
|
349
|
+
cockpit.events_path.parent.mkdir(parents=True, exist_ok=True)
|
|
350
|
+
cockpit.events_path.write_text("")
|
|
351
|
+
cockpit._panel_opened = False
|
|
352
|
+
branding.info("cockpit ON - the data window opens when research starts.")
|
|
353
|
+
else:
|
|
354
|
+
cockpit.status("done", "cockpit complete")
|
|
355
|
+
branding.info("cockpit OFF.")
|
|
356
|
+
continue
|
|
340
357
|
|
|
341
358
|
messages.append({"role": "user", "content": user_msg})
|
|
342
359
|
if deep_next:
|
|
@@ -354,12 +371,15 @@ def _agent_repl(backend: Backend, settings: Settings, renderer: Renderer) -> Non
|
|
|
354
371
|
auto_approve=auto,
|
|
355
372
|
stream=True,
|
|
356
373
|
guardian=guardian,
|
|
374
|
+
on_event=observer if cockpit_on else None,
|
|
357
375
|
)
|
|
358
376
|
if mode in ("reason", "escalate"):
|
|
359
377
|
branding.info("reasoned by CroweLM Reason · answered by DeepParallel")
|
|
360
378
|
except Exception as e: # noqa: BLE001 - surface as friendly message
|
|
361
379
|
renderer.error(_translate_error(e))
|
|
362
380
|
|
|
381
|
+
if cockpit_on:
|
|
382
|
+
cockpit.status("done", "cockpit complete")
|
|
363
383
|
branding.attribution_footer()
|
|
364
384
|
|
|
365
385
|
|
|
@@ -508,15 +528,7 @@ def cockpit(ctx: click.Context, assume_yes: bool, prompt: tuple[str, ...]) -> No
|
|
|
508
528
|
open(events_path, "w").close()
|
|
509
529
|
cp = Cockpit(events_path)
|
|
510
530
|
cp.status("start", "cockpit online")
|
|
511
|
-
|
|
512
|
-
try:
|
|
513
|
-
subprocess.Popen(
|
|
514
|
-
["wsh", "run", "--", "dp", "_cockpit-panel", events_path],
|
|
515
|
-
stdout=subprocess.DEVNULL,
|
|
516
|
-
stderr=subprocess.DEVNULL,
|
|
517
|
-
)
|
|
518
|
-
except Exception: # noqa: BLE001 - the panel block is best-effort
|
|
519
|
-
pass
|
|
531
|
+
cp.ensure_panel()
|
|
520
532
|
backend = _wrap_fusion(backend, settings)
|
|
521
533
|
system = load_system_prompt()
|
|
522
534
|
messages = _build_messages([], system, " ".join(prompt))
|
|
@@ -45,6 +45,7 @@ class Cockpit:
|
|
|
45
45
|
|
|
46
46
|
def __init__(self, events_path: str = ".cockpit/events.jsonl"):
|
|
47
47
|
self.events_path = Path(events_path)
|
|
48
|
+
self._panel_opened = False
|
|
48
49
|
|
|
49
50
|
# --- environment -------------------------------------------------------
|
|
50
51
|
|
|
@@ -119,6 +120,25 @@ class Cockpit:
|
|
|
119
120
|
"""Best-effort `wsh notify`. Silent no-op off WaveTerm or on failure."""
|
|
120
121
|
self._run_wsh(["notify", message])
|
|
121
122
|
|
|
123
|
+
def ensure_panel(self) -> None:
|
|
124
|
+
"""Open the live data-window block once per session (idempotent).
|
|
125
|
+
|
|
126
|
+
Spawns `dp _cockpit-panel <abs-path>` in an adjacent WaveTerm block on
|
|
127
|
+
the first call so the cockpit appears only when research actually
|
|
128
|
+
starts; repeat calls and runs outside WaveTerm are no-ops.
|
|
129
|
+
"""
|
|
130
|
+
if self._panel_opened or not self.in_waveterm():
|
|
131
|
+
return
|
|
132
|
+
self._panel_opened = True
|
|
133
|
+
try:
|
|
134
|
+
subprocess.Popen(
|
|
135
|
+
["wsh", "run", "--", "dp", "_cockpit-panel", str(self.events_path.resolve())],
|
|
136
|
+
stdout=subprocess.DEVNULL,
|
|
137
|
+
stderr=subprocess.DEVNULL,
|
|
138
|
+
)
|
|
139
|
+
except Exception: # noqa: BLE001 - the panel block is best-effort
|
|
140
|
+
pass
|
|
141
|
+
|
|
122
142
|
# --- contract event helpers -------------------------------------------
|
|
123
143
|
|
|
124
144
|
def status(self, phase: str, message: str) -> None:
|
|
@@ -31,8 +31,11 @@ def make_observer(cockpit) -> Callable[[str, dict, str], None]:
|
|
|
31
31
|
try:
|
|
32
32
|
if isinstance(obj, dict) and "error" not in obj:
|
|
33
33
|
if name == "web_search":
|
|
34
|
+
results = obj.get("results") or []
|
|
35
|
+
if results:
|
|
36
|
+
cockpit.ensure_panel()
|
|
34
37
|
provider = obj.get("provider", "web")
|
|
35
|
-
for r in
|
|
38
|
+
for r in results[:8]:
|
|
36
39
|
url = r.get("url")
|
|
37
40
|
if not url:
|
|
38
41
|
continue
|
|
@@ -45,11 +48,15 @@ def make_observer(cockpit) -> Callable[[str, dict, str], None]:
|
|
|
45
48
|
elif name == "web_fetch":
|
|
46
49
|
url = obj.get("url") or (args or {}).get("url", "")
|
|
47
50
|
if url:
|
|
51
|
+
cockpit.ensure_panel()
|
|
48
52
|
cockpit.source(url=url, title=obj.get("title", ""), provider="fetch")
|
|
49
53
|
if counters["blocks"] < _MAX_WEB_BLOCKS and cockpit.open_web(url):
|
|
50
54
|
counters["blocks"] += 1
|
|
51
55
|
elif name == "mcp_search":
|
|
52
|
-
|
|
56
|
+
servers = obj.get("servers") or []
|
|
57
|
+
if servers:
|
|
58
|
+
cockpit.ensure_panel()
|
|
59
|
+
for s in servers[:5]:
|
|
53
60
|
cockpit.source(url="", title=s.get("name", ""), provider="mcp")
|
|
54
61
|
cockpit.status("tool", name)
|
|
55
62
|
except Exception: # noqa: BLE001 - observation must never break the loop
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deepparallel
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.5
|
|
4
4
|
Summary: DeepParallel - a multi-model agentic coding CLI with cross-model Guardian review, served via Crowe Logic.
|
|
5
5
|
Author-email: Michael Crowe <michael@crowelogic.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "deepparallel"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.5"
|
|
8
8
|
description = "DeepParallel - a multi-model agentic coding CLI with cross-model Guardian review, served via Crowe Logic."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "Apache-2.0" }
|
|
@@ -239,3 +239,44 @@ def test_helpers_emit_contract_events(cockpit, off_wave):
|
|
|
239
239
|
assert by_type["citation"] == {"ref": 1, "url": "https://c", "title": "Cited"}
|
|
240
240
|
assert by_type["claim"] == {"text": "a claim", "grounded": True, "confidence": 0.9}
|
|
241
241
|
assert by_type["sim"] == {"step": 3, "arm": "A", "metric": "accuracy", "value": 0.42}
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
# --- native lazy panel ------------------------------------------------------
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def test_ensure_panel_noop_off_waveterm(cockpit, off_wave, monkeypatch):
|
|
248
|
+
calls = {"n": 0}
|
|
249
|
+
monkeypatch.setattr(subprocess, "Popen", lambda *a, **k: calls.__setitem__("n", calls["n"] + 1))
|
|
250
|
+
cockpit.ensure_panel()
|
|
251
|
+
assert calls["n"] == 0 # nothing spawned outside WaveTerm
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def test_ensure_panel_opens_once_in_waveterm(cockpit, in_wave, monkeypatch):
|
|
255
|
+
calls = {"n": 0}
|
|
256
|
+
monkeypatch.setattr(subprocess, "Popen", lambda *a, **k: calls.__setitem__("n", calls["n"] + 1))
|
|
257
|
+
cockpit.ensure_panel()
|
|
258
|
+
cockpit.ensure_panel()
|
|
259
|
+
cockpit.ensure_panel()
|
|
260
|
+
assert calls["n"] == 1 # idempotent: one panel per session
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def test_observer_opens_panel_on_grounding(cockpit, in_wave, monkeypatch):
|
|
264
|
+
from deepparallel.cockpit_observe import make_observer
|
|
265
|
+
|
|
266
|
+
calls = {"n": 0}
|
|
267
|
+
monkeypatch.setattr(subprocess, "Popen", lambda *a, **k: calls.__setitem__("n", calls["n"] + 1))
|
|
268
|
+
obs = make_observer(cockpit)
|
|
269
|
+
obs("web_search", {}, json.dumps({"provider": "web", "results": [{"url": "https://a", "title": "A"}]}))
|
|
270
|
+
assert calls["n"] == 1 # research triggers the data window
|
|
271
|
+
obs("web_search", {}, json.dumps({"provider": "web", "results": [{"url": "https://b", "title": "B"}]}))
|
|
272
|
+
assert calls["n"] == 1 # stays one
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def test_observer_no_panel_without_grounding(cockpit, in_wave, monkeypatch):
|
|
276
|
+
from deepparallel.cockpit_observe import make_observer
|
|
277
|
+
|
|
278
|
+
calls = {"n": 0}
|
|
279
|
+
monkeypatch.setattr(subprocess, "Popen", lambda *a, **k: calls.__setitem__("n", calls["n"] + 1))
|
|
280
|
+
obs = make_observer(cockpit)
|
|
281
|
+
obs("run_shell", {}, json.dumps({"return_code": 0, "stdout": "ok"})) # no grounding
|
|
282
|
+
assert calls["n"] == 0 # trivial tool use does not open the cockpit
|
|
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
|