vertracloud-api 0.2.0__tar.gz → 0.2.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.
- vertracloud_api-0.2.2/.release-please-manifest.json +1 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/CHANGELOG.md +14 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/PKG-INFO +18 -1
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/README.md +17 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/pyproject.toml +1 -1
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/src/vertracloud/__init__.py +6 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/src/vertracloud/client.py +1 -1
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/src/vertracloud/errors.py +19 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/src/vertracloud/resources/apps.py +30 -1
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/src/vertracloud/resources/blob.py +8 -1
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/src/vertracloud/types.py +11 -1
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/tests/test_blob.py +10 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/tests/test_multipart_and_binary.py +36 -0
- vertracloud_api-0.2.0/.release-please-manifest.json +0 -1
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/.github/workflows/ci.yml +0 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/.github/workflows/release.yml +0 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/.gitignore +0 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/LICENSE +0 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/release-please-config.json +0 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/src/vertracloud/py.typed +0 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/src/vertracloud/resources/__init__.py +0 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/src/vertracloud/resources/account.py +0 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/src/vertracloud/resources/billing.py +0 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/src/vertracloud/resources/databases.py +0 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/src/vertracloud/resources/snapshots.py +0 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/src/vertracloud/resources/workspaces.py +0 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/src/vertracloud/sse.py +0 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/src/vertracloud/transport.py +0 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/tests/__init__.py +0 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/tests/fake_transport.py +0 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/tests/routes_matrix.py +0 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/tests/test_client.py +0 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/tests/test_contract.py +0 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/tests/test_routes_matrix.py +0 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/tests/test_sse.py +0 -0
- {vertracloud_api-0.2.0 → vertracloud_api-0.2.2}/uv.lock +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{".":"0.2.2"}
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.2](https://github.com/vertracloud/sdk-api-py/compare/v0.2.1...v0.2.2) (2026-10-18)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* support async app creation (building status, wait_until_ready) ([044a73f](https://github.com/vertracloud/sdk-api-py/commit/044a73f981f540e1e070d52c0243469448243e46))
|
|
9
|
+
|
|
10
|
+
## [0.2.1](https://github.com/vertracloud/sdk-api-py/compare/v0.2.0...v0.2.1) (2026-10-15)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* **blob:** add rename_on_conflict to put and copy ([6773c4c](https://github.com/vertracloud/sdk-api-py/commit/6773c4cfa63032875dfe1b3f92967fe3128b4e7c))
|
|
16
|
+
|
|
3
17
|
## [0.2.0](https://github.com/vertracloud/sdk-api-py/compare/v0.1.5...v0.2.0) (2026-10-15)
|
|
4
18
|
|
|
5
19
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: vertracloud-api
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Official Python SDK for the public API of Vertra Cloud, a Brazilian application hosting platform (PaaS).
|
|
5
5
|
Project-URL: Homepage, https://vertracloud.app
|
|
6
6
|
Project-URL: Documentation, https://docs.vertracloud.app/sdks
|
|
@@ -142,6 +142,23 @@ with open("backup.zip", "wb") as f:
|
|
|
142
142
|
|
|
143
143
|
`client.snapshots.download` works the same way.
|
|
144
144
|
|
|
145
|
+
### Waiting for the first build
|
|
146
|
+
|
|
147
|
+
`apps.create` replies right away with `status: "building"`. Wait for the build to end:
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
app = client.apps.create({"name": "my-app", "memory": 512}, file=data)
|
|
151
|
+
try:
|
|
152
|
+
app = client.apps.wait_until_ready(app["id"], timeout=600, interval=2) # status is "up"
|
|
153
|
+
# ended "down": e.code is app["last_build_error"] (e.g. "INSTALL_OUT_OF_MEMORY"), "APP_DOWN" if none
|
|
154
|
+
except vertracloud.AppBuildFailedError as e:
|
|
155
|
+
print(client.apps.logs(app["id"]))
|
|
156
|
+
except vertracloud.WaitTimeoutError: # still building after `timeout` seconds
|
|
157
|
+
...
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`missing_dependencies` on the app is filled only after the build ends. Before it ends, calls that need a stopped or running app can fail with `APP_BUILDING` (409).
|
|
161
|
+
|
|
145
162
|
### Realtime logs (SSE)
|
|
146
163
|
|
|
147
164
|
```python
|
|
@@ -123,6 +123,23 @@ with open("backup.zip", "wb") as f:
|
|
|
123
123
|
|
|
124
124
|
`client.snapshots.download` works the same way.
|
|
125
125
|
|
|
126
|
+
### Waiting for the first build
|
|
127
|
+
|
|
128
|
+
`apps.create` replies right away with `status: "building"`. Wait for the build to end:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
app = client.apps.create({"name": "my-app", "memory": 512}, file=data)
|
|
132
|
+
try:
|
|
133
|
+
app = client.apps.wait_until_ready(app["id"], timeout=600, interval=2) # status is "up"
|
|
134
|
+
# ended "down": e.code is app["last_build_error"] (e.g. "INSTALL_OUT_OF_MEMORY"), "APP_DOWN" if none
|
|
135
|
+
except vertracloud.AppBuildFailedError as e:
|
|
136
|
+
print(client.apps.logs(app["id"]))
|
|
137
|
+
except vertracloud.WaitTimeoutError: # still building after `timeout` seconds
|
|
138
|
+
...
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
`missing_dependencies` on the app is filled only after the build ends. Before it ends, calls that need a stopped or running app can fail with `APP_BUILDING` (409).
|
|
142
|
+
|
|
126
143
|
### Realtime logs (SSE)
|
|
127
144
|
|
|
128
145
|
```python
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "vertracloud-api"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
description = "Official Python SDK for the public API of Vertra Cloud, a Brazilian application hosting platform (PaaS)."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
from .client import VertraClient, __version__
|
|
4
4
|
from .errors import (
|
|
5
|
+
AppBuildFailedError,
|
|
6
|
+
AppNotReadyError,
|
|
5
7
|
AuthenticationError,
|
|
6
8
|
NotFoundError,
|
|
7
9
|
RateLimitError,
|
|
8
10
|
ScopeDeniedError,
|
|
9
11
|
ValidationError,
|
|
10
12
|
VertraAPIError,
|
|
13
|
+
WaitTimeoutError,
|
|
11
14
|
)
|
|
12
15
|
from .sse import SseEvent, SseStream
|
|
13
16
|
from .transport import Response, Transport, UrllibTransport
|
|
@@ -16,6 +19,9 @@ __all__ = [
|
|
|
16
19
|
"VertraClient",
|
|
17
20
|
"__version__",
|
|
18
21
|
"VertraAPIError",
|
|
22
|
+
"AppNotReadyError",
|
|
23
|
+
"AppBuildFailedError",
|
|
24
|
+
"WaitTimeoutError",
|
|
19
25
|
"AuthenticationError",
|
|
20
26
|
"ScopeDeniedError",
|
|
21
27
|
"NotFoundError",
|
|
@@ -16,7 +16,7 @@ from .sse import SseStream
|
|
|
16
16
|
from .transport import Response, Transport, UrllibTransport
|
|
17
17
|
from .types import JsonValue
|
|
18
18
|
|
|
19
|
-
__version__ = "0.2.
|
|
19
|
+
__version__ = "0.2.2" # x-release-please-version
|
|
20
20
|
|
|
21
21
|
DEFAULT_BASE_URL = "https://api.vertracloud.app"
|
|
22
22
|
DEFAULT_BLOB_BASE_URL = "https://blob.vertracloud.app/v1"
|
|
@@ -70,6 +70,25 @@ class RateLimitError(VertraAPIError):
|
|
|
70
70
|
)
|
|
71
71
|
|
|
72
72
|
|
|
73
|
+
class AppNotReadyError(Exception):
|
|
74
|
+
"""`apps.wait_until_ready` ended without a running app. `app` is the last payload seen
|
|
75
|
+
(`None` on timeout before any reply). The cause of a `down` build is in `apps.logs`."""
|
|
76
|
+
|
|
77
|
+
def __init__(self, message: str, *, code: str, app: Any = None) -> None:
|
|
78
|
+
self.code = code
|
|
79
|
+
self.app = app
|
|
80
|
+
super().__init__(f"{code}: {message}")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class AppBuildFailedError(AppNotReadyError):
|
|
84
|
+
"""The build ended with the app `down`. `code` is the app's `last_build_error` (e.g.
|
|
85
|
+
`INSTALL_OUT_OF_MEMORY`), or `APP_DOWN` when the API sent none."""
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class WaitTimeoutError(AppNotReadyError):
|
|
89
|
+
"""`wait_until_ready` timed out while the app was still `building` (`code` is `WAIT_TIMEOUT`)."""
|
|
90
|
+
|
|
91
|
+
|
|
73
92
|
def error_from_response(status: int, body: dict[str, Any]) -> VertraAPIError:
|
|
74
93
|
"""Picks the class from the HTTP status; `code` comes from the error response body."""
|
|
75
94
|
code = str(body.get("code") or "UNKNOWN_ERROR")
|
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
+
import time
|
|
8
9
|
from typing import TYPE_CHECKING, Any
|
|
9
10
|
|
|
10
11
|
from ..client import LARGE_PAYLOAD_TIMEOUT, MultipartField, encode_path_param
|
|
12
|
+
from ..errors import AppBuildFailedError, WaitTimeoutError
|
|
11
13
|
from ..sse import SseStream
|
|
12
14
|
from ..types import (
|
|
13
15
|
APIAppFileUploadResponse,
|
|
@@ -442,7 +444,8 @@ class AppsResource:
|
|
|
442
444
|
) -> APIApplication:
|
|
443
445
|
"""POST /v1/apps — scope `apps:write`. Multipart: field `file` (zip) OR `snapshot_id`
|
|
444
446
|
in `body`, other fields as text parts. Rate limit 15/min, plus the plan's
|
|
445
|
-
deploys-per-hour cap.
|
|
447
|
+
deploys-per-hour cap. Replies 202 with `status: "building"`: the build continues in the
|
|
448
|
+
background — use `wait_until_ready`, `logs` or `realtime` to follow it.
|
|
446
449
|
Doc: https://docs.vertracloud.app/api-reference/endpoint/apps/create
|
|
447
450
|
"""
|
|
448
451
|
fields: list[MultipartField] = []
|
|
@@ -463,6 +466,32 @@ class AppsResource:
|
|
|
463
466
|
"POST", "/v1/apps", fields=fields, timeout=timeout if timeout is not None else LARGE_PAYLOAD_TIMEOUT
|
|
464
467
|
)
|
|
465
468
|
|
|
469
|
+
def wait_until_ready(
|
|
470
|
+
self,
|
|
471
|
+
app_id: str,
|
|
472
|
+
*,
|
|
473
|
+
timeout: float = 600.0,
|
|
474
|
+
interval: float = 2.0,
|
|
475
|
+
workspace_id: str | None = None,
|
|
476
|
+
) -> APIApplication:
|
|
477
|
+
"""Polls `GET /v1/apps/:id` while `status` is `building`. Returns the app when it is `up`;
|
|
478
|
+
raises `AppBuildFailedError` when it ends `down` and `WaitTimeoutError` after `timeout`
|
|
479
|
+
seconds. API errors from the polling propagate unchanged."""
|
|
480
|
+
deadline = time.monotonic() + timeout
|
|
481
|
+
while True:
|
|
482
|
+
app = self.get(app_id, workspace_id=workspace_id)
|
|
483
|
+
if app["status"] == "up":
|
|
484
|
+
return app
|
|
485
|
+
if app["status"] == "down":
|
|
486
|
+
raise AppBuildFailedError(
|
|
487
|
+
"the build ended and the app is down; read apps.logs for the cause",
|
|
488
|
+
code=app.get("last_build_error") or "APP_DOWN",
|
|
489
|
+
app=app,
|
|
490
|
+
)
|
|
491
|
+
if time.monotonic() + interval > deadline:
|
|
492
|
+
raise WaitTimeoutError(f"still building after {timeout:g}s", code="WAIT_TIMEOUT", app=app)
|
|
493
|
+
time.sleep(interval)
|
|
494
|
+
|
|
466
495
|
def start(self, app_id: str, *, workspace_id: str | None = None) -> APIApplicationOperationResponse:
|
|
467
496
|
"""POST /v1/apps/:id/start — scope `apps:write`."""
|
|
468
497
|
return self._c.request_json(
|
|
@@ -240,6 +240,7 @@ class BlobResource:
|
|
|
240
240
|
visibility: BlobVisibility | None = None,
|
|
241
241
|
overwrite: bool | None = None,
|
|
242
242
|
random_suffix: bool | None = None,
|
|
243
|
+
rename_on_conflict: bool | None = None,
|
|
243
244
|
cache_control: str | None = None,
|
|
244
245
|
content_disposition: str | None = None,
|
|
245
246
|
filename: str | None = None,
|
|
@@ -253,11 +254,14 @@ class BlobResource:
|
|
|
253
254
|
Up to 5 MB: `POST /objects` (multipart). Larger: `POST /uploads`, then a presigned
|
|
254
255
|
PUT (single) or parts (multipart) and `/complete`. `on_progress(loaded, total)` is
|
|
255
256
|
called as bytes are sent. If a large upload is interrupted, call `resume`.
|
|
257
|
+
`rename_on_conflict=True` saves a taken path as `name-1.ext`, `name-2.ext`, … (not with `overwrite`);
|
|
258
|
+
the returned object has the final path.
|
|
256
259
|
"""
|
|
257
260
|
opts: dict[str, Any] = {
|
|
258
261
|
"visibility": visibility,
|
|
259
262
|
"overwrite": overwrite,
|
|
260
263
|
"random_suffix": random_suffix,
|
|
264
|
+
"rename_on_conflict": rename_on_conflict,
|
|
261
265
|
"cache_control": cache_control,
|
|
262
266
|
"content_disposition": content_disposition,
|
|
263
267
|
"filename": filename,
|
|
@@ -428,7 +432,10 @@ class BlobResource:
|
|
|
428
432
|
return results
|
|
429
433
|
|
|
430
434
|
def copy(self, id: str, path: str, *, workspace_id: str | None = None, **opts: Any) -> APIBlobObject:
|
|
431
|
-
"""POST /objects/copy — `overwrite`, `random_suffix`, `visibility`, `move` as options.
|
|
435
|
+
"""POST /objects/copy — `overwrite`, `random_suffix`, `visibility`, `move` as options.
|
|
436
|
+
|
|
437
|
+
`rename_on_conflict=True` (copy only) saves a taken path, even the source's own, as `name-1.ext`.
|
|
438
|
+
"""
|
|
432
439
|
return self._json(
|
|
433
440
|
"POST", "/objects/copy", json_body={"source": id, "path": path, **opts}, query=_ws(workspace_id)
|
|
434
441
|
)
|
|
@@ -249,7 +249,16 @@ class PutBillingDetailsBody(_PutBillingDetailsRequired, total=False):
|
|
|
249
249
|
ApplicationLanguage = Literal[
|
|
250
250
|
"javascript", "typescript", "bun", "python", "static", "php", "go", "ruby", "java", "rust"
|
|
251
251
|
]
|
|
252
|
-
ApplicationStatus = Literal["up", "down"]
|
|
252
|
+
ApplicationStatus = Literal["up", "down", "building"]
|
|
253
|
+
ApplicationBuildError = Literal[
|
|
254
|
+
"BUILD_FAILED",
|
|
255
|
+
"BUILD_TIMEOUT",
|
|
256
|
+
"BUILD_INTERRUPTED",
|
|
257
|
+
"INSTALL_FAILED",
|
|
258
|
+
"INSTALL_OUT_OF_MEMORY",
|
|
259
|
+
"INSTALL_TIMEOUT",
|
|
260
|
+
"INSTALL_SHIELD_BLOCKED",
|
|
261
|
+
]
|
|
253
262
|
ApplicationFileType = Literal["file", "directory"]
|
|
254
263
|
DatabaseStatus = Literal["up", "down"]
|
|
255
264
|
ResourceType = Literal[1, 2]
|
|
@@ -298,6 +307,7 @@ class _APIApplicationRequired(TypedDict):
|
|
|
298
307
|
offline_since: str | None
|
|
299
308
|
shield_cooldown: APIApplicationShieldCooldown | None
|
|
300
309
|
deploy: APIApplicationDeploySettings
|
|
310
|
+
last_build_error: ApplicationBuildError | None
|
|
301
311
|
|
|
302
312
|
|
|
303
313
|
class APIApplication(_APIApplicationRequired, total=False):
|
|
@@ -74,6 +74,16 @@ def test_put_small_is_multipart_on_objects() -> None:
|
|
|
74
74
|
assert len(t.calls) == 1
|
|
75
75
|
|
|
76
76
|
|
|
77
|
+
def test_put_and_copy_send_rename_on_conflict() -> None:
|
|
78
|
+
client, t = _setup()
|
|
79
|
+
t.queue_json({"response": OBJ})
|
|
80
|
+
t.queue_json({"response": OBJ})
|
|
81
|
+
client.blob.put("a/b.txt", b"hi", rename_on_conflict=True)
|
|
82
|
+
assert _q(t.calls[0])["rename_on_conflict"] == ["true"]
|
|
83
|
+
client.blob.copy("o1", "a/b.txt", rename_on_conflict=True)
|
|
84
|
+
assert json.loads(t.calls[1].body or b"{}") == {"source": "o1", "path": "a/b.txt", "rename_on_conflict": True}
|
|
85
|
+
|
|
86
|
+
|
|
77
87
|
def test_put_from_path(tmp_path: Path) -> None:
|
|
78
88
|
f = tmp_path / "x.txt"
|
|
79
89
|
f.write_bytes(b"from-disk")
|
|
@@ -118,3 +118,39 @@ def test_snapshot_download_returns_bytes() -> None:
|
|
|
118
118
|
transport.queue_binary(b"zip-content")
|
|
119
119
|
client = VertraClient(api_key="k", transport=transport)
|
|
120
120
|
assert client.snapshots.download("user-1", "snap-1", "applications") == b"zip-content"
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _app(status: str) -> dict:
|
|
124
|
+
return {"response": {"id": "app-1", "status": status}}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def test_wait_until_ready_returns_app_when_up() -> None:
|
|
128
|
+
from vertracloud import VertraClient
|
|
129
|
+
|
|
130
|
+
transport = FakeTransport()
|
|
131
|
+
transport.queue_json(_app("building"))
|
|
132
|
+
transport.queue_json(_app("up"))
|
|
133
|
+
client = VertraClient(api_key="k", transport=transport)
|
|
134
|
+
assert client.apps.wait_until_ready("app-1", interval=0)["status"] == "up"
|
|
135
|
+
assert len(transport.calls) == 2
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def test_wait_until_ready_raises_on_down_and_timeout() -> None:
|
|
139
|
+
from vertracloud import AppBuildFailedError, VertraClient, WaitTimeoutError
|
|
140
|
+
|
|
141
|
+
transport = FakeTransport()
|
|
142
|
+
transport.queue_json(_app("down"))
|
|
143
|
+
client = VertraClient(api_key="k", transport=transport)
|
|
144
|
+
with pytest.raises(AppBuildFailedError) as down:
|
|
145
|
+
client.apps.wait_until_ready("app-1", interval=0)
|
|
146
|
+
assert down.value.code == "APP_DOWN"
|
|
147
|
+
|
|
148
|
+
transport.queue_json({"response": {"id": "app-1", "status": "down", "last_build_error": "INSTALL_OUT_OF_MEMORY"}})
|
|
149
|
+
with pytest.raises(AppBuildFailedError) as oom:
|
|
150
|
+
client.apps.wait_until_ready("app-1", interval=0)
|
|
151
|
+
assert oom.value.code == "INSTALL_OUT_OF_MEMORY"
|
|
152
|
+
|
|
153
|
+
transport.default_json = _app("building")
|
|
154
|
+
with pytest.raises(WaitTimeoutError) as slow:
|
|
155
|
+
client.apps.wait_until_ready("app-1", timeout=0, interval=0)
|
|
156
|
+
assert slow.value.code == "WAIT_TIMEOUT"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{".":"0.2.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
|