meshagent-api 0.53.0__tar.gz → 0.53.2__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.
- {meshagent_api-0.53.0/meshagent_api.egg-info → meshagent_api-0.53.2}/PKG-INFO +1 -1
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/client.py +98 -11
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/client_test.py +79 -1
- meshagent_api-0.53.2/meshagent/api/version.py +1 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2/meshagent_api.egg-info}/PKG-INFO +1 -1
- meshagent_api-0.53.0/meshagent/api/version.py +0 -1
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/LICENSE +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/MANIFEST.in +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/README.md +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/__init__.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/agent_content.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/aiohttp_redaction.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/chan.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/crdt.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/dataset_schema.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/entrypoint.js +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/error_codes.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/helpers.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/http.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/image_runtime.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/keys.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/llm_router_config.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/managed_agents.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/managed_agents_test.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/messaging.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/messaging_test.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/mime_types.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/mime_types_test.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/oauth.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/oauth_branding.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/oauth_scopes.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/oauth_scopes_test.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/participant.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/participant_token.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/participant_token_test.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/port_forward.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/port_forward_test.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/protocol.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/protocol_test.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/py.typed +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/reasoning_schema.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/registry_auth.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/room_ports.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/room_server_client.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/room_server_client_test.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/runtime.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/runtime_test.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/schema.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/schema_document.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/schema_document_test.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/schema_registry.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/schema_test.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/schema_util.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/service_spec_test.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/service_template_test.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/services.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/services_test.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/specs/service.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/sql.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/sql_test.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/sqlite_transfer.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/sqlite_transfer_test.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/token_test.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/urls.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/webhooks.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/webhooks_test.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/websocket_protocol.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent/api/websocket_protocol_test.py +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent_api.egg-info/SOURCES.txt +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent_api.egg-info/dependency_links.txt +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent_api.egg-info/requires.txt +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/meshagent_api.egg-info/top_level.txt +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/pyproject.toml +0 -0
- {meshagent_api-0.53.0 → meshagent_api-0.53.2}/setup.cfg +0 -0
|
@@ -722,6 +722,27 @@ class UpdateUserProfileRequest(BaseModel):
|
|
|
722
722
|
annotations: dict[str, str] = Field(default_factory=dict)
|
|
723
723
|
|
|
724
724
|
|
|
725
|
+
UserProfileView = Literal["project", "user", "merged"]
|
|
726
|
+
UserProfileField = Literal["first_name", "last_name", "metadata", "annotations"]
|
|
727
|
+
|
|
728
|
+
|
|
729
|
+
class UpdateProjectUserProfileRequest(UpdateUserProfileRequest):
|
|
730
|
+
inherit: list[UserProfileField] = Field(default_factory=list)
|
|
731
|
+
|
|
732
|
+
@model_validator(mode="after")
|
|
733
|
+
def validate_inherited_fields(self) -> "UpdateProjectUserProfileRequest":
|
|
734
|
+
if set(self.inherit) & self.model_fields_set:
|
|
735
|
+
raise ValueError(
|
|
736
|
+
"A field cannot be supplied and inherited in the same update"
|
|
737
|
+
)
|
|
738
|
+
return self
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
class UserProfilesPage(BaseModel):
|
|
742
|
+
users: list[User]
|
|
743
|
+
continuation_token: str | None = None
|
|
744
|
+
|
|
745
|
+
|
|
725
746
|
class UserRoomGrant(BaseModel):
|
|
726
747
|
room: Room
|
|
727
748
|
user: User
|
|
@@ -1558,13 +1579,14 @@ class Meshagent:
|
|
|
1558
1579
|
page_size: int = 100,
|
|
1559
1580
|
continuation_token: str | None = None,
|
|
1560
1581
|
filter: str | None = None,
|
|
1582
|
+
view: UserProfileView = "merged",
|
|
1561
1583
|
) -> ProjectMembersPage:
|
|
1562
1584
|
"""
|
|
1563
1585
|
Corresponds to: GET /accounts/projects/:id/users
|
|
1564
1586
|
Returns a JSON dict with { "users": [...] }.
|
|
1565
1587
|
"""
|
|
1566
1588
|
url = f"{self.base_url}/accounts/projects/{project_id}/users"
|
|
1567
|
-
params: Dict[str, str] = {"page_size": str(page_size)}
|
|
1589
|
+
params: Dict[str, str] = {"page_size": str(page_size), "view": view}
|
|
1568
1590
|
if continuation_token is not None:
|
|
1569
1591
|
params["continuation_token"] = continuation_token
|
|
1570
1592
|
if filter is not None and filter.strip() != "":
|
|
@@ -1580,18 +1602,37 @@ class Meshagent:
|
|
|
1580
1602
|
except ValidationError as exc:
|
|
1581
1603
|
raise RoomException(f"Invalid project users payload: {exc}") from exc
|
|
1582
1604
|
|
|
1583
|
-
async def get_users_in_project(
|
|
1584
|
-
|
|
1585
|
-
|
|
1605
|
+
async def get_users_in_project(
|
|
1606
|
+
self, project_id: str, *, view: UserProfileView = "merged"
|
|
1607
|
+
) -> Dict[str, Any]:
|
|
1608
|
+
page = await self.get_users_in_project_page(project_id, view=view)
|
|
1609
|
+
return page.model_dump(mode="json", exclude_unset=view == "project")
|
|
1586
1610
|
|
|
1587
|
-
async def get_user_profile(
|
|
1611
|
+
async def get_user_profile(
|
|
1612
|
+
self,
|
|
1613
|
+
user_id: str,
|
|
1614
|
+
*,
|
|
1615
|
+
project_id: str | None = None,
|
|
1616
|
+
view: UserProfileView = "merged",
|
|
1617
|
+
) -> Dict[str, Any]:
|
|
1588
1618
|
"""
|
|
1589
1619
|
Corresponds to: GET /accounts/profiles/:id
|
|
1590
1620
|
Returns the user profile JSON, e.g. { "id", "first_name", "last_name", "email" } or raises 404 if not found.
|
|
1591
1621
|
"""
|
|
1592
|
-
|
|
1622
|
+
user_path = quote(user_id, safe="")
|
|
1623
|
+
if project_id is None:
|
|
1624
|
+
if view == "project":
|
|
1625
|
+
raise ValueError("project_id is required for the project view")
|
|
1626
|
+
url = f"{self.base_url}/accounts/profiles/{user_path}"
|
|
1627
|
+
params = None
|
|
1628
|
+
else:
|
|
1629
|
+
project_path = quote(project_id, safe="")
|
|
1630
|
+
url = f"{self.base_url}/accounts/projects/{project_path}/users/{user_path}/profile"
|
|
1631
|
+
params = {"view": view}
|
|
1593
1632
|
|
|
1594
|
-
async with self._session.get(
|
|
1633
|
+
async with self._session.get(
|
|
1634
|
+
url, headers=self._get_headers(), params=params
|
|
1635
|
+
) as resp:
|
|
1595
1636
|
await self._raise_for_status(resp)
|
|
1596
1637
|
return await resp.json()
|
|
1597
1638
|
|
|
@@ -1604,15 +1645,17 @@ class Meshagent:
|
|
|
1604
1645
|
metadata: dict[str, JsonValue] | None = None,
|
|
1605
1646
|
annotations: dict[str, str] | None = None,
|
|
1606
1647
|
project_id: str | None = None,
|
|
1648
|
+
inherit: list[UserProfileField] | None = None,
|
|
1607
1649
|
) -> Dict[str, Any]:
|
|
1608
1650
|
"""
|
|
1609
1651
|
Corresponds to: PUT /accounts/profiles/:id
|
|
1610
1652
|
Omitted fields are preserved; metadata and annotations replace their maps.
|
|
1611
|
-
|
|
1612
|
-
|
|
1653
|
+
With project_id, edits project overrides and requires user_profile_editor,
|
|
1654
|
+
including for your own profile. Without a project, edits your global profile.
|
|
1613
1655
|
Returns a JSON dict with { "ok": True } on success.
|
|
1614
1656
|
"""
|
|
1615
|
-
|
|
1657
|
+
user_path = quote(user_id, safe="")
|
|
1658
|
+
url = f"{self.base_url}/accounts/profiles/{user_path}"
|
|
1616
1659
|
body: dict[str, Any] = {}
|
|
1617
1660
|
if first_name is not None:
|
|
1618
1661
|
body["first_name"] = first_name
|
|
@@ -1623,7 +1666,12 @@ class Meshagent:
|
|
|
1623
1666
|
if annotations is not None:
|
|
1624
1667
|
body["annotations"] = annotations
|
|
1625
1668
|
if project_id is not None:
|
|
1626
|
-
|
|
1669
|
+
project_path = quote(project_id, safe="")
|
|
1670
|
+
url = f"{self.base_url}/accounts/projects/{project_path}/users/{user_path}/profile"
|
|
1671
|
+
if inherit is not None:
|
|
1672
|
+
if project_id is None:
|
|
1673
|
+
raise ValueError("project_id is required to inherit profile fields")
|
|
1674
|
+
body["inherit"] = inherit
|
|
1627
1675
|
|
|
1628
1676
|
async with self._session.put(
|
|
1629
1677
|
url, headers=self._get_headers(), json=body
|
|
@@ -1631,6 +1679,45 @@ class Meshagent:
|
|
|
1631
1679
|
await self._raise_for_status(resp)
|
|
1632
1680
|
return await resp.json()
|
|
1633
1681
|
|
|
1682
|
+
async def search_sysadmin_users(
|
|
1683
|
+
self,
|
|
1684
|
+
*,
|
|
1685
|
+
filter: str | None = None,
|
|
1686
|
+
page_size: int = 100,
|
|
1687
|
+
continuation_token: str | None = None,
|
|
1688
|
+
) -> UserProfilesPage:
|
|
1689
|
+
params = {"page_size": str(page_size)}
|
|
1690
|
+
if filter is not None:
|
|
1691
|
+
params["filter"] = filter
|
|
1692
|
+
if continuation_token is not None:
|
|
1693
|
+
params["continuation_token"] = continuation_token
|
|
1694
|
+
async with self._session.get(
|
|
1695
|
+
f"{self.base_url}/accounts/sysadmin/users",
|
|
1696
|
+
headers=self._get_headers(),
|
|
1697
|
+
params=params,
|
|
1698
|
+
) as resp:
|
|
1699
|
+
await self._raise_for_status(resp)
|
|
1700
|
+
return UserProfilesPage.model_validate(await resp.json())
|
|
1701
|
+
|
|
1702
|
+
async def get_sysadmin_user_profile(self, user_id: str) -> User:
|
|
1703
|
+
async with self._session.get(
|
|
1704
|
+
f"{self.base_url}/accounts/sysadmin/users/{quote(user_id, safe='')}",
|
|
1705
|
+
headers=self._get_headers(),
|
|
1706
|
+
) as resp:
|
|
1707
|
+
await self._raise_for_status(resp)
|
|
1708
|
+
return User.model_validate(await resp.json())
|
|
1709
|
+
|
|
1710
|
+
async def update_sysadmin_user_profile(
|
|
1711
|
+
self, user_id: str, *, update: UpdateUserProfileRequest
|
|
1712
|
+
) -> dict[str, Any]:
|
|
1713
|
+
async with self._session.put(
|
|
1714
|
+
f"{self.base_url}/accounts/sysadmin/users/{quote(user_id, safe='')}",
|
|
1715
|
+
headers=self._get_headers(),
|
|
1716
|
+
json=update.model_dump(mode="json", exclude_unset=True),
|
|
1717
|
+
) as resp:
|
|
1718
|
+
await self._raise_for_status(resp)
|
|
1719
|
+
return await resp.json()
|
|
1720
|
+
|
|
1634
1721
|
async def list_projects(self) -> ProjectsPage:
|
|
1635
1722
|
"""
|
|
1636
1723
|
Corresponds to: GET /accounts/projects
|
|
@@ -2764,7 +2764,7 @@ async def test_update_user_profile_supports_partial_metadata_and_editor_context(
|
|
|
2764
2764
|
("put", "http://example.test/accounts/profiles/me", {"metadata": {}}),
|
|
2765
2765
|
(
|
|
2766
2766
|
"put",
|
|
2767
|
-
"http://example.test/accounts/
|
|
2767
|
+
"http://example.test/accounts/projects/project-1/users/user-2/profile",
|
|
2768
2768
|
{
|
|
2769
2769
|
"first_name": "Grace",
|
|
2770
2770
|
"last_name": "Hopper",
|
|
@@ -2792,3 +2792,81 @@ def test_user_profile_models_preserve_json_metadata_and_string_annotations():
|
|
|
2792
2792
|
assert User(id="old-user", email="old@example.test").metadata == {}
|
|
2793
2793
|
update = UpdateUserProfileRequest.model_validate({"metadata": {}})
|
|
2794
2794
|
assert update.model_dump(exclude_unset=True) == {"metadata": {}}
|
|
2795
|
+
|
|
2796
|
+
|
|
2797
|
+
@pytest.mark.asyncio
|
|
2798
|
+
@pytest.mark.parametrize("view", ["project", "user", "merged"])
|
|
2799
|
+
async def test_project_profile_reads_use_project_endpoint_and_preserve_raw_fields(view):
|
|
2800
|
+
payload = {
|
|
2801
|
+
"id": "user-1",
|
|
2802
|
+
"email": "a@example.test",
|
|
2803
|
+
"first_name": None,
|
|
2804
|
+
"metadata": {"local": True},
|
|
2805
|
+
}
|
|
2806
|
+
session = _FakeSession([_FakeResponse(status=200, payload=payload)])
|
|
2807
|
+
client = Meshagent(base_url="http://example.test", token="token", session=session)
|
|
2808
|
+
assert (
|
|
2809
|
+
await client.get_user_profile("user-1", project_id="project-1", view=view)
|
|
2810
|
+
== payload
|
|
2811
|
+
)
|
|
2812
|
+
assert session.calls == [
|
|
2813
|
+
(
|
|
2814
|
+
"get",
|
|
2815
|
+
"http://example.test/accounts/projects/project-1/users/user-1/profile",
|
|
2816
|
+
{"view": view},
|
|
2817
|
+
)
|
|
2818
|
+
]
|
|
2819
|
+
|
|
2820
|
+
|
|
2821
|
+
@pytest.mark.asyncio
|
|
2822
|
+
async def test_project_profile_inheritance_and_global_sysadmin_routes():
|
|
2823
|
+
from meshagent.api.client import UpdateUserProfileRequest
|
|
2824
|
+
|
|
2825
|
+
session = _FakeSession(
|
|
2826
|
+
[
|
|
2827
|
+
_FakeResponse(status=200, payload={"ok": True}),
|
|
2828
|
+
_FakeResponse(
|
|
2829
|
+
status=200, payload={"users": [], "continuation_token": None}
|
|
2830
|
+
),
|
|
2831
|
+
_FakeResponse(status=200, payload={"ok": True}),
|
|
2832
|
+
]
|
|
2833
|
+
)
|
|
2834
|
+
client = Meshagent(base_url="http://example.test", token="token", session=session)
|
|
2835
|
+
await client.update_user_profile(
|
|
2836
|
+
"me", project_id="project-1", inherit=["first_name", "metadata"]
|
|
2837
|
+
)
|
|
2838
|
+
await client.search_sysadmin_users(filter="Ada", page_size=10)
|
|
2839
|
+
await client.update_sysadmin_user_profile(
|
|
2840
|
+
"user-1", update=UpdateUserProfileRequest(annotations={})
|
|
2841
|
+
)
|
|
2842
|
+
assert session.calls == [
|
|
2843
|
+
(
|
|
2844
|
+
"put",
|
|
2845
|
+
"http://example.test/accounts/projects/project-1/users/me/profile",
|
|
2846
|
+
{"inherit": ["first_name", "metadata"]},
|
|
2847
|
+
),
|
|
2848
|
+
(
|
|
2849
|
+
"get",
|
|
2850
|
+
"http://example.test/accounts/sysadmin/users",
|
|
2851
|
+
{"page_size": "10", "filter": "Ada"},
|
|
2852
|
+
),
|
|
2853
|
+
(
|
|
2854
|
+
"put",
|
|
2855
|
+
"http://example.test/accounts/sysadmin/users/user-1",
|
|
2856
|
+
{"annotations": {}},
|
|
2857
|
+
),
|
|
2858
|
+
]
|
|
2859
|
+
with pytest.raises(ValueError):
|
|
2860
|
+
await client.get_user_profile("me", view="project")
|
|
2861
|
+
with pytest.raises(ValueError):
|
|
2862
|
+
await client.update_user_profile("me", inherit=["metadata"])
|
|
2863
|
+
|
|
2864
|
+
|
|
2865
|
+
@pytest.mark.asyncio
|
|
2866
|
+
async def test_sysadmin_profile_access_denials_are_raised():
|
|
2867
|
+
from meshagent.api.client import PermissionDeniedError
|
|
2868
|
+
|
|
2869
|
+
session = _FakeSession([_FakeResponse(status=403, payload={"error": "forbidden"})])
|
|
2870
|
+
client = Meshagent(base_url="http://example.test", token="token", session=session)
|
|
2871
|
+
with pytest.raises(PermissionDeniedError):
|
|
2872
|
+
await client.get_sysadmin_user_profile("user-1")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.53.2"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.53.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|