codex-ipc 0.1.2__tar.gz → 0.1.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.
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/PKG-INFO +1 -1
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/ipc/src/ipc/constants.py +8 -3
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/ipc/src/ipc/owner_follower.py +83 -4
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/pyproject.toml +1 -1
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/tests/test_plan_mode_user_input.py +109 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/uv.lock +1 -1
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/.github/workflows/publish.yml +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/.gitignore +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/.python-version +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/.vscode/launch.json +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/README.md +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/examples/demo.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/examples/tui.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/codex-sdk/README.md +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/codex-sdk/pyproject.toml +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/codex-sdk/src/codex_app_server/__init__.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/codex-sdk/src/codex_app_server/_inputs.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/codex-sdk/src/codex_app_server/_message_router.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/codex-sdk/src/codex_app_server/_run.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/codex-sdk/src/codex_app_server/_version.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/codex-sdk/src/codex_app_server/api.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/codex-sdk/src/codex_app_server/async_client.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/codex-sdk/src/codex_app_server/client.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/codex-sdk/src/codex_app_server/errors.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/codex-sdk/src/codex_app_server/generated/__init__.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/codex-sdk/src/codex_app_server/generated/notification_registry.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/codex-sdk/src/codex_app_server/generated/v2_all.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/codex-sdk/src/codex_app_server/models.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/codex-sdk/src/codex_app_server/py.typed +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/codex-sdk/src/codex_app_server/retry.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/ipc/README.md +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/ipc/pyproject.toml +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/ipc/src/ipc/__init__.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/ipc/src/ipc/client.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/ipc/src/ipc/conversation_state.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/ipc/src/ipc/frame_protocol.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/ipc/src/ipc/immer.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/ipc/src/ipc/py.typed +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/ipc/src/ipc/router.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/src/codex_ipc/__init__.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/src/codex_ipc/codex/__init__.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/src/codex_ipc/codex/api.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/src/codex_ipc/codex/async_client.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/src/codex_ipc/codex/client.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/src/codex_ipc/codex/generated/__init__.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/src/codex_ipc/codex/generated/v2_all.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/src/codex_ipc/config.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/src/codex_ipc/session.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/src/codex_ipc/user_input.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/tests/__init__.py +0 -0
- {codex_ipc-0.1.2 → codex_ipc-0.1.3}/tests/test_ssh_websocket_transport.py +0 -0
|
@@ -22,7 +22,10 @@ IPC_DEBUG_TEXT_LIMIT = 500
|
|
|
22
22
|
|
|
23
23
|
IPC_METHOD_VERSIONS: dict[str, int] = {
|
|
24
24
|
# broadcast methods
|
|
25
|
-
"thread-stream-state-changed":
|
|
25
|
+
"thread-stream-state-changed": 11,
|
|
26
|
+
"thread-stream-following-changed": 1,
|
|
27
|
+
"thread-stream-following-status-requested": 1,
|
|
28
|
+
"ipc-connection-reset": 1,
|
|
26
29
|
"thread-read-state-changed": 1,
|
|
27
30
|
"thread-archived": 2,
|
|
28
31
|
"thread-unarchived": 1,
|
|
@@ -31,12 +34,14 @@ IPC_METHOD_VERSIONS: dict[str, int] = {
|
|
|
31
34
|
"client-status-changed": 0,
|
|
32
35
|
# request methods (thread-follower series)
|
|
33
36
|
"thread-follower-start-turn": 1,
|
|
37
|
+
"thread-follower-load-complete-history": 1,
|
|
34
38
|
"thread-follower-compact-thread": 1,
|
|
35
39
|
"thread-follower-steer-turn": 1,
|
|
36
|
-
"thread-follower-interrupt-turn":
|
|
40
|
+
"thread-follower-interrupt-turn": 2,
|
|
41
|
+
"thread-follower-update-thread-settings": 1,
|
|
37
42
|
"thread-follower-set-model-and-reasoning": 1,
|
|
38
43
|
"thread-follower-set-collaboration-mode": 1,
|
|
39
|
-
"thread-follower-edit-last-user-turn":
|
|
44
|
+
"thread-follower-edit-last-user-turn": 2,
|
|
40
45
|
"thread-follower-command-approval-decision": 1,
|
|
41
46
|
"thread-follower-file-approval-decision": 1,
|
|
42
47
|
"thread-follower-permissions-request-approval-response": 1,
|
|
@@ -149,7 +149,7 @@ class OwnerFollowerSession:
|
|
|
149
149
|
self._state: JsonDict | None = None
|
|
150
150
|
self._stream_role: JsonDict | None = None
|
|
151
151
|
self._streaming_conversations: set[str] = set()
|
|
152
|
-
# Monotonic per-conversation revision (matches Codex extension
|
|
152
|
+
# Monotonic per-conversation revision (matches Codex extension v11
|
|
153
153
|
# streamRevisionByConversationId). Owner increments on every snapshot
|
|
154
154
|
# and patch it emits; followers track it to guard out-of-order patches.
|
|
155
155
|
self._stream_revision: int = 0
|
|
@@ -206,6 +206,11 @@ class OwnerFollowerSession:
|
|
|
206
206
|
self._can_handle_owner_request,
|
|
207
207
|
self._handle_follower_start_turn,
|
|
208
208
|
)
|
|
209
|
+
self.ipc_client.add_request_handler(
|
|
210
|
+
"thread-follower-load-complete-history",
|
|
211
|
+
self._can_handle_owner_request,
|
|
212
|
+
self._handle_follower_load_complete_history,
|
|
213
|
+
)
|
|
209
214
|
self.ipc_client.add_request_handler(
|
|
210
215
|
"thread-follower-steer-turn",
|
|
211
216
|
self._can_handle_owner_request,
|
|
@@ -216,6 +221,11 @@ class OwnerFollowerSession:
|
|
|
216
221
|
self._can_handle_owner_request,
|
|
217
222
|
self._handle_follower_set_queued_followups_state,
|
|
218
223
|
)
|
|
224
|
+
self.ipc_client.add_request_handler(
|
|
225
|
+
"thread-follower-update-thread-settings",
|
|
226
|
+
self._can_handle_owner_request,
|
|
227
|
+
self._handle_follower_update_thread_settings,
|
|
228
|
+
)
|
|
219
229
|
self.ipc_client.add_request_handler(
|
|
220
230
|
"thread-follower-set-collaboration-mode",
|
|
221
231
|
self._can_handle_owner_request,
|
|
@@ -333,17 +343,17 @@ class OwnerFollowerSession:
|
|
|
333
343
|
owner_client_id = self._owner_client_id()
|
|
334
344
|
if owner_client_id:
|
|
335
345
|
response = await self.ipc_client.send_request(
|
|
336
|
-
"thread-follower-
|
|
346
|
+
"thread-follower-update-thread-settings",
|
|
337
347
|
{
|
|
338
348
|
"conversationId": self.thread_id,
|
|
339
|
-
"collaborationMode": collaboration_mode,
|
|
349
|
+
"threadSettings": {"collaborationMode": collaboration_mode},
|
|
340
350
|
},
|
|
341
351
|
target_client_id=owner_client_id,
|
|
342
352
|
)
|
|
343
353
|
if response.get("resultType") == "success":
|
|
344
354
|
return
|
|
345
355
|
raise RuntimeError(
|
|
346
|
-
"thread-follower-
|
|
356
|
+
"thread-follower-update-thread-settings failed: "
|
|
347
357
|
f"{response.get('error', '')}"
|
|
348
358
|
)
|
|
349
359
|
|
|
@@ -2992,6 +3002,19 @@ class OwnerFollowerSession:
|
|
|
2992
3002
|
}
|
|
2993
3003
|
}
|
|
2994
3004
|
|
|
3005
|
+
async def _handle_follower_load_complete_history(
|
|
3006
|
+
self,
|
|
3007
|
+
message: JsonDict,
|
|
3008
|
+
) -> JsonDict:
|
|
3009
|
+
params = message.get("params")
|
|
3010
|
+
if not isinstance(params, dict):
|
|
3011
|
+
raise RuntimeError("invalid params")
|
|
3012
|
+
if params.get("conversationId") != self.thread_id:
|
|
3013
|
+
raise RuntimeError("invalid conversationId")
|
|
3014
|
+
|
|
3015
|
+
await self.read_thread(include_turns=True, broadcast_snapshot=True)
|
|
3016
|
+
return {"revision": self._stream_revision}
|
|
3017
|
+
|
|
2995
3018
|
async def _handle_follower_steer_turn(self, message: JsonDict) -> JsonDict:
|
|
2996
3019
|
params = message.get("params")
|
|
2997
3020
|
if not isinstance(params, dict):
|
|
@@ -3028,6 +3051,62 @@ class OwnerFollowerSession:
|
|
|
3028
3051
|
)
|
|
3029
3052
|
return {"result": {"ok": True}}
|
|
3030
3053
|
|
|
3054
|
+
async def _handle_follower_update_thread_settings(
|
|
3055
|
+
self,
|
|
3056
|
+
message: JsonDict,
|
|
3057
|
+
) -> JsonDict:
|
|
3058
|
+
params = message.get("params")
|
|
3059
|
+
if not isinstance(params, dict):
|
|
3060
|
+
raise RuntimeError("invalid params")
|
|
3061
|
+
if params.get("conversationId") != self.thread_id:
|
|
3062
|
+
raise RuntimeError("invalid conversationId")
|
|
3063
|
+
|
|
3064
|
+
thread_settings = params.get("threadSettings")
|
|
3065
|
+
if not isinstance(thread_settings, dict):
|
|
3066
|
+
raise RuntimeError("invalid threadSettings")
|
|
3067
|
+
|
|
3068
|
+
await self._update_state(
|
|
3069
|
+
lambda draft: self._apply_thread_settings(draft, thread_settings)
|
|
3070
|
+
)
|
|
3071
|
+
return {"result": {"ok": True}}
|
|
3072
|
+
|
|
3073
|
+
def _apply_thread_settings(
|
|
3074
|
+
self,
|
|
3075
|
+
draft: JsonDict,
|
|
3076
|
+
thread_settings: JsonDict,
|
|
3077
|
+
) -> None:
|
|
3078
|
+
latest_settings = draft.get("latestThreadSettings")
|
|
3079
|
+
merged_settings = (
|
|
3080
|
+
dict(latest_settings) if isinstance(latest_settings, dict) else {}
|
|
3081
|
+
)
|
|
3082
|
+
merged_settings.update(thread_settings)
|
|
3083
|
+
|
|
3084
|
+
collaboration_mode = thread_settings.get("collaborationMode")
|
|
3085
|
+
if isinstance(collaboration_mode, dict):
|
|
3086
|
+
merged_settings["collaborationMode"] = dict(collaboration_mode)
|
|
3087
|
+
draft["latestCollaborationMode"] = dict(collaboration_mode)
|
|
3088
|
+
elif "collaborationMode" in thread_settings:
|
|
3089
|
+
collaboration_mode = default_collaboration_mode()
|
|
3090
|
+
merged_settings["collaborationMode"] = collaboration_mode
|
|
3091
|
+
draft["latestCollaborationMode"] = collaboration_mode
|
|
3092
|
+
|
|
3093
|
+
model = thread_settings.get("model")
|
|
3094
|
+
if not isinstance(model, str) and isinstance(collaboration_mode, dict):
|
|
3095
|
+
settings = collaboration_mode.get("settings")
|
|
3096
|
+
if isinstance(settings, dict) and isinstance(settings.get("model"), str):
|
|
3097
|
+
model = settings["model"]
|
|
3098
|
+
if isinstance(model, str):
|
|
3099
|
+
merged_settings["model"] = model
|
|
3100
|
+
draft["latestModel"] = model
|
|
3101
|
+
|
|
3102
|
+
if "effort" in thread_settings:
|
|
3103
|
+
draft["latestReasoningEffort"] = thread_settings.get("effort")
|
|
3104
|
+
cwd = thread_settings.get("cwd")
|
|
3105
|
+
if isinstance(cwd, str):
|
|
3106
|
+
draft["cwd"] = cwd
|
|
3107
|
+
|
|
3108
|
+
draft["latestThreadSettings"] = merged_settings
|
|
3109
|
+
|
|
3031
3110
|
async def _handle_follower_submit_user_input(
|
|
3032
3111
|
self,
|
|
3033
3112
|
message: JsonDict,
|
|
@@ -44,6 +44,7 @@ from ipc.conversation_state import (
|
|
|
44
44
|
build_conversation_state,
|
|
45
45
|
default_collaboration_mode,
|
|
46
46
|
)
|
|
47
|
+
from ipc.constants import IPC_METHOD_VERSIONS
|
|
47
48
|
from ipc.owner_follower import OwnerFollowerSession
|
|
48
49
|
|
|
49
50
|
|
|
@@ -570,6 +571,21 @@ class FacadeSessionTests(unittest.IsolatedAsyncioTestCase):
|
|
|
570
571
|
|
|
571
572
|
|
|
572
573
|
class CoreImportTests(unittest.TestCase):
|
|
574
|
+
def test_ipc_versions_match_conversation_state_v11(self) -> None:
|
|
575
|
+
expected_versions = {
|
|
576
|
+
"thread-stream-state-changed": 11,
|
|
577
|
+
"thread-stream-following-changed": 1,
|
|
578
|
+
"thread-stream-following-status-requested": 1,
|
|
579
|
+
"ipc-connection-reset": 1,
|
|
580
|
+
"thread-follower-load-complete-history": 1,
|
|
581
|
+
"thread-follower-interrupt-turn": 2,
|
|
582
|
+
"thread-follower-update-thread-settings": 1,
|
|
583
|
+
"thread-follower-edit-last-user-turn": 2,
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
for method, version in expected_versions.items():
|
|
587
|
+
self.assertEqual(IPC_METHOD_VERSIONS[method], version)
|
|
588
|
+
|
|
573
589
|
def test_core_import_does_not_require_tui_dependencies(self) -> None:
|
|
574
590
|
script = """
|
|
575
591
|
import builtins
|
|
@@ -597,6 +613,99 @@ print(codex_ipc.CodexIpcConfig.__name__)
|
|
|
597
613
|
|
|
598
614
|
|
|
599
615
|
class PlanModeTests(unittest.IsolatedAsyncioTestCase):
|
|
616
|
+
async def test_follower_forwards_collaboration_mode_as_thread_settings(self) -> None:
|
|
617
|
+
session = make_session()
|
|
618
|
+
ipc_client = FakeIpcClient()
|
|
619
|
+
session.ipc_client = ipc_client
|
|
620
|
+
session._stream_role = {"role": "follower", "ownerClientId": "owner-1"}
|
|
621
|
+
collaboration_mode = {
|
|
622
|
+
"mode": "plan",
|
|
623
|
+
"settings": {
|
|
624
|
+
"model": "gpt-5.5",
|
|
625
|
+
"reasoning_effort": "high",
|
|
626
|
+
"developer_instructions": None,
|
|
627
|
+
},
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
await session.set_collaboration_mode(collaboration_mode)
|
|
631
|
+
|
|
632
|
+
self.assertEqual(
|
|
633
|
+
ipc_client.requests,
|
|
634
|
+
[
|
|
635
|
+
(
|
|
636
|
+
"thread-follower-update-thread-settings",
|
|
637
|
+
{
|
|
638
|
+
"conversationId": "thread-1",
|
|
639
|
+
"threadSettings": {"collaborationMode": collaboration_mode},
|
|
640
|
+
},
|
|
641
|
+
"owner-1",
|
|
642
|
+
)
|
|
643
|
+
],
|
|
644
|
+
)
|
|
645
|
+
|
|
646
|
+
async def test_owner_applies_follower_thread_settings(self) -> None:
|
|
647
|
+
session = make_session()
|
|
648
|
+
session.ipc_client = FakeIpcClient()
|
|
649
|
+
session._stream_role = {"role": "owner"}
|
|
650
|
+
session._state = {
|
|
651
|
+
"id": "thread-1",
|
|
652
|
+
"latestModel": "gpt-5.4",
|
|
653
|
+
"latestReasoningEffort": "medium",
|
|
654
|
+
"latestCollaborationMode": default_collaboration_mode(),
|
|
655
|
+
"latestThreadSettings": {"model": "gpt-5.4"},
|
|
656
|
+
"updatedAt": 0,
|
|
657
|
+
}
|
|
658
|
+
collaboration_mode = {
|
|
659
|
+
"mode": "plan",
|
|
660
|
+
"settings": {
|
|
661
|
+
"model": "gpt-5.5",
|
|
662
|
+
"reasoning_effort": "high",
|
|
663
|
+
"developer_instructions": None,
|
|
664
|
+
},
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
result = await session._handle_follower_update_thread_settings(
|
|
668
|
+
{
|
|
669
|
+
"params": {
|
|
670
|
+
"conversationId": "thread-1",
|
|
671
|
+
"threadSettings": {
|
|
672
|
+
"collaborationMode": collaboration_mode,
|
|
673
|
+
"effort": "high",
|
|
674
|
+
"cwd": "/repo",
|
|
675
|
+
},
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
)
|
|
679
|
+
|
|
680
|
+
self.assertEqual(result, {"result": {"ok": True}})
|
|
681
|
+
self.assertEqual(session.state["latestCollaborationMode"], collaboration_mode)
|
|
682
|
+
self.assertEqual(session.state["latestModel"], "gpt-5.5")
|
|
683
|
+
self.assertEqual(session.state["latestReasoningEffort"], "high")
|
|
684
|
+
self.assertEqual(session.state["cwd"], "/repo")
|
|
685
|
+
|
|
686
|
+
async def test_owner_loads_complete_history_and_returns_snapshot_revision(self) -> None:
|
|
687
|
+
session = make_session()
|
|
688
|
+
calls: list[tuple[bool, bool]] = []
|
|
689
|
+
|
|
690
|
+
async def read_thread(
|
|
691
|
+
_thread_id: str | None = None,
|
|
692
|
+
*,
|
|
693
|
+
include_turns: bool = False,
|
|
694
|
+
broadcast_snapshot: bool = False,
|
|
695
|
+
) -> object:
|
|
696
|
+
calls.append((include_turns, broadcast_snapshot))
|
|
697
|
+
session._stream_revision = 8
|
|
698
|
+
return object()
|
|
699
|
+
|
|
700
|
+
session.read_thread = read_thread # type: ignore[method-assign]
|
|
701
|
+
|
|
702
|
+
result = await session._handle_follower_load_complete_history(
|
|
703
|
+
{"params": {"conversationId": "thread-1"}}
|
|
704
|
+
)
|
|
705
|
+
|
|
706
|
+
self.assertEqual(calls, [(True, True)])
|
|
707
|
+
self.assertEqual(result, {"revision": 8})
|
|
708
|
+
|
|
600
709
|
async def test_turn_start_passes_collaboration_mode(self) -> None:
|
|
601
710
|
codex = FakeCodex()
|
|
602
711
|
thread = AsyncThread(codex, "thread-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
|
{codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/codex-sdk/src/codex_app_server/_message_router.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/codex-sdk/src/codex_app_server/generated/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{codex_ipc-0.1.2 → codex_ipc-0.1.3}/packages/codex-sdk/src/codex_app_server/generated/v2_all.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|