hypercli-sdk 2026.4.18__tar.gz → 2026.4.20__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.
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/PKG-INFO +1 -1
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/hypercli/__init__.py +1 -1
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/hypercli/agent.py +51 -1
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/hypercli/agents.py +15 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/pyproject.toml +1 -1
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/tests/test_agents.py +42 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/tests/test_claw.py +102 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/.gitignore +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/README.md +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/hypercli/billing.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/hypercli/client.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/hypercli/config.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/hypercli/files.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/hypercli/gateway.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/hypercli/http.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/hypercli/instances.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/hypercli/job/__init__.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/hypercli/job/base.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/hypercli/job/comfyui.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/hypercli/job/gradio.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/hypercli/jobs.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/hypercli/keys.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/hypercli/logs.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/hypercli/models.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/hypercli/openclaw/__init__.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/hypercli/openclaw/gateway.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/hypercli/renders.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/hypercli/shell.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/hypercli/user.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/hypercli/voice.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/hypercli/x402.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/tests/integration/conftest.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/tests/integration/test_agents.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/tests/integration/test_auth.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/tests/integration/test_billing.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/tests/integration/test_instances.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/tests/integration/test_jobs_dryrun.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/tests/integration/test_keys.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/tests/integration/test_renders.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/tests/test_apply_params.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/tests/test_bootstrap_console_test_key.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/tests/test_bootstrap_dev_test_keys.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/tests/test_config.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/tests/test_exec_shell_dryrun.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/tests/test_gateway.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/tests/test_gateway_retry.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/tests/test_graph_to_api.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/tests/test_http.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/tests/test_jobs.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/tests/test_keys.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/tests/test_models.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/tests/test_renders_subscription.py +0 -0
- {hypercli_sdk-2026.4.18 → hypercli_sdk-2026.4.20}/tests/test_voice.py +0 -0
|
@@ -7,7 +7,7 @@ Uses the official OpenAI Python client for chat completions.
|
|
|
7
7
|
from dataclasses import dataclass
|
|
8
8
|
from datetime import datetime
|
|
9
9
|
from typing import Any, Dict, List, Optional, Union
|
|
10
|
-
from urllib.parse import urlsplit
|
|
10
|
+
from urllib.parse import quote, urlsplit
|
|
11
11
|
|
|
12
12
|
from .config import get_agents_api_base_url
|
|
13
13
|
from .http import HTTPClient
|
|
@@ -266,6 +266,7 @@ class HyperAgentSubscriptionSummary:
|
|
|
266
266
|
active_subscription_count: int
|
|
267
267
|
active_entitlement_count: int
|
|
268
268
|
entitlements: HyperAgentEntitlements
|
|
269
|
+
entitlement_items: list[HyperAgentEntitlement]
|
|
269
270
|
active_subscriptions: list[HyperAgentSubscription]
|
|
270
271
|
subscriptions: list[HyperAgentSubscription]
|
|
271
272
|
user: dict[str, Any]
|
|
@@ -286,6 +287,7 @@ class HyperAgentSubscriptionSummary:
|
|
|
286
287
|
active_subscription_count=int(data.get("active_subscription_count", 0) or 0),
|
|
287
288
|
active_entitlement_count=int(data.get("active_entitlement_count", data.get("active_subscription_count", 0)) or 0),
|
|
288
289
|
entitlements=HyperAgentEntitlements.from_dict(data),
|
|
290
|
+
entitlement_items=[HyperAgentEntitlement.from_dict(item) for item in data.get("entitlement_items", [])],
|
|
289
291
|
active_subscriptions=[HyperAgentSubscription.from_dict(item) for item in data.get("active_subscriptions", [])],
|
|
290
292
|
subscriptions=[HyperAgentSubscription.from_dict(item) for item in data.get("subscriptions", [])],
|
|
291
293
|
user=data.get("user") or {},
|
|
@@ -909,6 +911,54 @@ class HyperAgent:
|
|
|
909
911
|
response.raise_for_status()
|
|
910
912
|
return response.json()
|
|
911
913
|
|
|
914
|
+
def purchase_via_x402(
|
|
915
|
+
self,
|
|
916
|
+
plan_id: str,
|
|
917
|
+
*,
|
|
918
|
+
quantity: int | None = None,
|
|
919
|
+
bundle: dict[str, int] | None = None,
|
|
920
|
+
) -> HyperAgentX402CheckoutResponse:
|
|
921
|
+
payload: dict[str, Any] = {}
|
|
922
|
+
if quantity is not None:
|
|
923
|
+
payload["quantity"] = int(quantity)
|
|
924
|
+
if bundle is not None:
|
|
925
|
+
payload["bundle"] = {str(k): int(v) for k, v in bundle.items()}
|
|
926
|
+
response = self._http._session.post(
|
|
927
|
+
f"{self._control_base_url}/x402/{quote(str(plan_id), safe='')}",
|
|
928
|
+
headers={"Authorization": f"Bearer {self._api_key}"},
|
|
929
|
+
json=payload,
|
|
930
|
+
)
|
|
931
|
+
response.raise_for_status()
|
|
932
|
+
return HyperAgentX402CheckoutResponse.from_dict(response.json())
|
|
933
|
+
|
|
934
|
+
def purchase_bundle_via_x402(
|
|
935
|
+
self,
|
|
936
|
+
*,
|
|
937
|
+
quantity: int | None = None,
|
|
938
|
+
bundle: dict[str, int] | None = None,
|
|
939
|
+
) -> HyperAgentX402CheckoutResponse:
|
|
940
|
+
payload: dict[str, Any] = {}
|
|
941
|
+
if quantity is not None:
|
|
942
|
+
payload["quantity"] = int(quantity)
|
|
943
|
+
if bundle is not None:
|
|
944
|
+
payload["bundle"] = {str(k): int(v) for k, v in bundle.items()}
|
|
945
|
+
response = self._http._session.post(
|
|
946
|
+
f"{self._control_base_url}/x402/_bundle",
|
|
947
|
+
headers={"Authorization": f"Bearer {self._api_key}"},
|
|
948
|
+
json=payload,
|
|
949
|
+
)
|
|
950
|
+
response.raise_for_status()
|
|
951
|
+
return HyperAgentX402CheckoutResponse.from_dict(response.json())
|
|
952
|
+
|
|
953
|
+
def create_x402_checkout(
|
|
954
|
+
self,
|
|
955
|
+
*,
|
|
956
|
+
quantity: int | None = None,
|
|
957
|
+
bundle: dict[str, int] | None = None,
|
|
958
|
+
) -> HyperAgentX402CheckoutResponse:
|
|
959
|
+
"""Backward-compatible bundle x402 checkout shim."""
|
|
960
|
+
return self.purchase_bundle_via_x402(quantity=quantity, bundle=bundle)
|
|
961
|
+
|
|
912
962
|
def discovery_health(self) -> Dict[str, Any]:
|
|
913
963
|
response = self._http._session.get(f"{self._api_base_without_v1()}/discovery/health")
|
|
914
964
|
response.raise_for_status()
|
|
@@ -1350,6 +1350,21 @@ class Deployments:
|
|
|
1350
1350
|
payload = {"name": name} if name is not None else {}
|
|
1351
1351
|
return self._post(f"{AGENTS_API_PREFIX}/{agent_id}/keys", json=payload or None)
|
|
1352
1352
|
|
|
1353
|
+
def purchase_entitlement_from_balance(
|
|
1354
|
+
self,
|
|
1355
|
+
plan_id: str,
|
|
1356
|
+
*,
|
|
1357
|
+
duration: int,
|
|
1358
|
+
tags: list[str] | None = None,
|
|
1359
|
+
) -> dict:
|
|
1360
|
+
payload: dict[str, Any] = {"duration": int(duration)}
|
|
1361
|
+
if tags is not None:
|
|
1362
|
+
payload["tags"] = list(tags)
|
|
1363
|
+
return self._post(f"/billing/balance/{quote(str(plan_id), safe='')}", json=payload)
|
|
1364
|
+
|
|
1365
|
+
def redeem_grant_code(self, code: str) -> dict:
|
|
1366
|
+
return self._post("/billing/grants/redeem", json={"code": str(code)})
|
|
1367
|
+
|
|
1353
1368
|
def logs_token(self, agent_id: str) -> dict:
|
|
1354
1369
|
"""Mint a short-lived JWT token for backend log streaming."""
|
|
1355
1370
|
return self._post(f"{AGENTS_API_PREFIX}/{agent_id}/logs/token")
|
|
@@ -1034,6 +1034,48 @@ def test_agents_create_scoped_key(agents_client):
|
|
|
1034
1034
|
assert mock_client.post.call_args[1]["json"] == {"name": "agent-client"}
|
|
1035
1035
|
|
|
1036
1036
|
|
|
1037
|
+
def test_agents_purchase_entitlement_from_balance(agents_client):
|
|
1038
|
+
with patch("httpx.Client") as mock_client_class:
|
|
1039
|
+
mock_client = MagicMock()
|
|
1040
|
+
mock_response = Mock()
|
|
1041
|
+
mock_response.status_code = 200
|
|
1042
|
+
mock_response.json.return_value = {
|
|
1043
|
+
"grant": {"id": "grant-1", "type": "BALANCE", "duration": 3600},
|
|
1044
|
+
"entitlement": {"id": "ent-1", "plan_id": "1aiu"},
|
|
1045
|
+
}
|
|
1046
|
+
mock_client.post.return_value = mock_response
|
|
1047
|
+
mock_client.__enter__.return_value = mock_client
|
|
1048
|
+
mock_client.__exit__.return_value = False
|
|
1049
|
+
mock_client_class.return_value = mock_client
|
|
1050
|
+
|
|
1051
|
+
result = agents_client.purchase_entitlement_from_balance("1aiu", duration=3600, tags=["customer=acme"])
|
|
1052
|
+
|
|
1053
|
+
assert result["grant"]["type"] == "BALANCE"
|
|
1054
|
+
assert mock_client.post.call_args[0][0].endswith("/billing/balance/1aiu")
|
|
1055
|
+
assert mock_client.post.call_args[1]["json"] == {"duration": 3600, "tags": ["customer=acme"]}
|
|
1056
|
+
|
|
1057
|
+
|
|
1058
|
+
def test_agents_redeem_grant_code(agents_client):
|
|
1059
|
+
with patch("httpx.Client") as mock_client_class:
|
|
1060
|
+
mock_client = MagicMock()
|
|
1061
|
+
mock_response = Mock()
|
|
1062
|
+
mock_response.status_code = 200
|
|
1063
|
+
mock_response.json.return_value = {
|
|
1064
|
+
"grant": {"id": "grant-1", "type": "ACTIVATION_CODE", "code": "promo-123"},
|
|
1065
|
+
"entitlement": {"id": "ent-1", "plan_id": "1aiu"},
|
|
1066
|
+
}
|
|
1067
|
+
mock_client.post.return_value = mock_response
|
|
1068
|
+
mock_client.__enter__.return_value = mock_client
|
|
1069
|
+
mock_client.__exit__.return_value = False
|
|
1070
|
+
mock_client_class.return_value = mock_client
|
|
1071
|
+
|
|
1072
|
+
result = agents_client.redeem_grant_code("promo-123")
|
|
1073
|
+
|
|
1074
|
+
assert result["grant"]["code"] == "promo-123"
|
|
1075
|
+
assert mock_client.post.call_args[0][0].endswith("/billing/grants/redeem")
|
|
1076
|
+
assert mock_client.post.call_args[1]["json"] == {"code": "promo-123"}
|
|
1077
|
+
|
|
1078
|
+
|
|
1037
1079
|
def test_openclaw_agent_resolve_gateway_token_uses_inference_endpoint():
|
|
1038
1080
|
manager = Mock()
|
|
1039
1081
|
manager.inference_token.return_value = {
|
|
@@ -12,6 +12,7 @@ from hypercli.agent import (
|
|
|
12
12
|
HyperAgentPlan,
|
|
13
13
|
HyperAgentCurrentPlan,
|
|
14
14
|
HyperAgentSubscription,
|
|
15
|
+
HyperAgentEntitlement,
|
|
15
16
|
HyperAgentSubscriptionSummary,
|
|
16
17
|
HyperAgentModel,
|
|
17
18
|
HyperAgentUsageSummary,
|
|
@@ -99,6 +100,23 @@ class TestHyperAgentDataclasses:
|
|
|
99
100
|
"slot_inventory": {"large": {"granted": 2, "used": 1, "available": 1}},
|
|
100
101
|
"active_entitlement_count": 1,
|
|
101
102
|
},
|
|
103
|
+
"entitlement_items": [
|
|
104
|
+
{
|
|
105
|
+
"id": "ent-1",
|
|
106
|
+
"user_id": "user-1",
|
|
107
|
+
"subscription_id": "sub-1",
|
|
108
|
+
"plan_id": "large",
|
|
109
|
+
"plan_name": "Large",
|
|
110
|
+
"provider": "STRIPE",
|
|
111
|
+
"status": "ACTIVE",
|
|
112
|
+
"expires_at": "2026-04-15T00:00:00Z",
|
|
113
|
+
"agent_tier": "large",
|
|
114
|
+
"features": {"voice": True},
|
|
115
|
+
"tags": ["customer=acme"],
|
|
116
|
+
"active_agent_count": 1,
|
|
117
|
+
"active_agent_ids": ["agent-1"],
|
|
118
|
+
}
|
|
119
|
+
],
|
|
102
120
|
"active_subscriptions": [
|
|
103
121
|
{
|
|
104
122
|
"id": "sub-1",
|
|
@@ -369,6 +387,90 @@ class TestHyperAgentClient:
|
|
|
369
387
|
max_tokens=100
|
|
370
388
|
)
|
|
371
389
|
|
|
390
|
+
def test_purchase_via_x402_uses_plan_route(self, mock_http):
|
|
391
|
+
agent = HyperAgent(
|
|
392
|
+
mock_http,
|
|
393
|
+
agent_api_key="sk-hyper-test",
|
|
394
|
+
agents_api_base_url="https://api.hypercli.com/agents",
|
|
395
|
+
)
|
|
396
|
+
mock_response = Mock()
|
|
397
|
+
mock_response.raise_for_status.return_value = None
|
|
398
|
+
mock_response.json.return_value = {
|
|
399
|
+
"ok": True,
|
|
400
|
+
"key": "hyper_api_x402",
|
|
401
|
+
"plan_id": "1aiu",
|
|
402
|
+
"quantity": 1,
|
|
403
|
+
"bundle": {"small": 1},
|
|
404
|
+
"amount_paid": "20.00",
|
|
405
|
+
"duration_days": 30,
|
|
406
|
+
"expires_at": "2026-05-19T12:00:00Z",
|
|
407
|
+
"tpm_limit": 1000,
|
|
408
|
+
"rpm_limit": 10,
|
|
409
|
+
}
|
|
410
|
+
mock_http._session.post.return_value = mock_response
|
|
411
|
+
|
|
412
|
+
result = agent.purchase_via_x402("1aiu", quantity=1, bundle={"small": 1})
|
|
413
|
+
|
|
414
|
+
assert result.plan_id == "1aiu"
|
|
415
|
+
assert mock_http._session.post.call_args[0][0] == "https://api.hypercli.com/agents/x402/1aiu"
|
|
416
|
+
assert mock_http._session.post.call_args[1]["json"] == {"quantity": 1, "bundle": {"small": 1}}
|
|
417
|
+
|
|
418
|
+
def test_purchase_bundle_via_x402_uses_bundle_route(self, mock_http):
|
|
419
|
+
agent = HyperAgent(
|
|
420
|
+
mock_http,
|
|
421
|
+
agent_api_key="sk-hyper-test",
|
|
422
|
+
agents_api_base_url="https://api.hypercli.com/agents",
|
|
423
|
+
)
|
|
424
|
+
mock_response = Mock()
|
|
425
|
+
mock_response.raise_for_status.return_value = None
|
|
426
|
+
mock_response.json.return_value = {
|
|
427
|
+
"ok": True,
|
|
428
|
+
"key": "hyper_api_x402",
|
|
429
|
+
"plan_id": "_bundle",
|
|
430
|
+
"quantity": 1,
|
|
431
|
+
"bundle": {"large": 2},
|
|
432
|
+
"amount_paid": "200.00",
|
|
433
|
+
"duration_days": 30,
|
|
434
|
+
"expires_at": "2026-05-19T12:00:00Z",
|
|
435
|
+
"tpm_limit": 1000,
|
|
436
|
+
"rpm_limit": 10,
|
|
437
|
+
}
|
|
438
|
+
mock_http._session.post.return_value = mock_response
|
|
439
|
+
|
|
440
|
+
result = agent.purchase_bundle_via_x402(quantity=1, bundle={"large": 2})
|
|
441
|
+
|
|
442
|
+
assert result.plan_id == "_bundle"
|
|
443
|
+
assert mock_http._session.post.call_args[0][0] == "https://api.hypercli.com/agents/x402/_bundle"
|
|
444
|
+
assert mock_http._session.post.call_args[1]["json"] == {"quantity": 1, "bundle": {"large": 2}}
|
|
445
|
+
|
|
446
|
+
def test_create_x402_checkout_is_bundle_shim(self, mock_http):
|
|
447
|
+
agent = HyperAgent(
|
|
448
|
+
mock_http,
|
|
449
|
+
agent_api_key="sk-hyper-test",
|
|
450
|
+
agents_api_base_url="https://api.hypercli.com/agents",
|
|
451
|
+
)
|
|
452
|
+
mock_response = Mock()
|
|
453
|
+
mock_response.raise_for_status.return_value = None
|
|
454
|
+
mock_response.json.return_value = {
|
|
455
|
+
"ok": True,
|
|
456
|
+
"key": "hyper_api_x402",
|
|
457
|
+
"plan_id": "_bundle",
|
|
458
|
+
"quantity": 1,
|
|
459
|
+
"bundle": {"medium": 1},
|
|
460
|
+
"amount_paid": "40.00",
|
|
461
|
+
"duration_days": 30,
|
|
462
|
+
"expires_at": "2026-05-19T12:00:00Z",
|
|
463
|
+
"tpm_limit": 1000,
|
|
464
|
+
"rpm_limit": 10,
|
|
465
|
+
}
|
|
466
|
+
mock_http._session.post.return_value = mock_response
|
|
467
|
+
|
|
468
|
+
result = agent.create_x402_checkout(quantity=1, bundle={"medium": 1})
|
|
469
|
+
|
|
470
|
+
assert result.plan_id == "_bundle"
|
|
471
|
+
assert mock_http._session.post.call_args[0][0] == "https://api.hypercli.com/agents/x402/_bundle"
|
|
472
|
+
assert mock_http._session.post.call_args[1]["json"] == {"quantity": 1, "bundle": {"medium": 1}}
|
|
473
|
+
|
|
372
474
|
class TestHyperAgentIntegration:
|
|
373
475
|
"""Integration tests for HyperAgent client (require running service)."""
|
|
374
476
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|