vers-sdk 0.1.1__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.
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 2.4
2
+ Name: vers-sdk
3
+ Version: 0.1.1
4
+ Summary: SDK for Orchestrator Control Plane API
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://github.com/hdresearch/python-sdk
7
+ Project-URL: Repository, https://github.com/hdresearch/python-sdk
8
+ Keywords: vers,sdk,api,vm,orchestration
9
+ Requires-Python: >=3.10
10
+ Requires-Dist: httpx>=0.24
@@ -0,0 +1,81 @@
1
+ # Vers Python API Library
2
+
3
+ This library provides convenient access to the [Vers](https://hdr.is) REST API from Python.
4
+
5
+ It is generated with [Sterling](https://github.com/hdresearch/sterling).
6
+
7
+ ## Installation
8
+
9
+ ```sh
10
+ pip install vers-sdk
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```python
16
+ import asyncio
17
+ from vers_sdk import VersSdkClient
18
+
19
+ async def main():
20
+ client = VersSdkClient(
21
+ api_key="your-api-key", # or set VERS_API_KEY env var
22
+ )
23
+
24
+ # List all VMs
25
+ response = await client.list_vms()
26
+ print(response.json())
27
+
28
+ # Create a new root VM
29
+ vm = await client.create_new_root_vm(body={"vm_config": {}})
30
+ print(vm.json())
31
+
32
+ await client.close()
33
+
34
+ asyncio.run(main())
35
+ ```
36
+
37
+ ## Configuration
38
+
39
+ ```python
40
+ client = VersSdkClient(
41
+ api_key="your-api-key", # or set VERS_API_KEY env var
42
+ base_url="https://api.vers.sh", # or set VERS_BASE_URL env var
43
+ max_retries=2,
44
+ timeout=30.0,
45
+ http_client=custom_httpx_client, # optional custom httpx.AsyncClient
46
+ )
47
+ ```
48
+
49
+ ## Error handling
50
+
51
+ ```python
52
+ from vers_sdk.errors import APIError, NotFoundError, RateLimitError
53
+
54
+ try:
55
+ await client.delete_vm("nonexistent-id")
56
+ except NotFoundError as e:
57
+ print(f"Not found: {e.status} {e.message}")
58
+ except APIError as e:
59
+ print(f"API error: {e.status}")
60
+ ```
61
+
62
+ ## Retries
63
+
64
+ Requests are automatically retried up to 2 times on 5xx errors and connection failures,
65
+ with exponential backoff and jitter. The client respects `Retry-After` headers (capped at 60s).
66
+
67
+ ## Async context manager
68
+
69
+ ```python
70
+ async with VersSdkClient(api_key="...") as client:
71
+ vms = await client.list_vms()
72
+ ```
73
+
74
+ ## Requirements
75
+
76
+ - Python 3.10+
77
+ - httpx
78
+
79
+ ## License
80
+
81
+ MIT
@@ -0,0 +1,19 @@
1
+ [project]
2
+ name = "vers-sdk"
3
+ version = "0.1.1"
4
+ description = "SDK for Orchestrator Control Plane API"
5
+ requires-python = ">=3.10"
6
+ license = "MIT"
7
+ keywords = ["vers", "sdk", "api", "vm", "orchestration"]
8
+ dependencies = ["httpx>=0.24"]
9
+
10
+ [project.urls]
11
+ Homepage = "https://github.com/hdresearch/python-sdk"
12
+ Repository = "https://github.com/hdresearch/python-sdk"
13
+
14
+ [build-system]
15
+ requires = ["setuptools>=68.0"]
16
+ build-backend = "setuptools.build_meta"
17
+
18
+ [tool.setuptools.packages.find]
19
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,34 @@
1
+ # Generated by Sterling SDK Generator
2
+ # Orchestrator Control Plane API v0.1.0
3
+
4
+ from .client import VersSdkClient
5
+ from .errors import (
6
+ VersSDKError,
7
+ APIError,
8
+ APIConnectionError,
9
+ APIConnectionTimeoutError,
10
+ BadRequestError,
11
+ AuthenticationError,
12
+ PermissionDeniedError,
13
+ NotFoundError,
14
+ ConflictError,
15
+ UnprocessableEntityError,
16
+ RateLimitError,
17
+ InternalServerError,
18
+ )
19
+
20
+ __all__ = [
21
+ "VersSdkClient",
22
+ "VersSDKError",
23
+ "APIError",
24
+ "APIConnectionError",
25
+ "APIConnectionTimeoutError",
26
+ "BadRequestError",
27
+ "AuthenticationError",
28
+ "PermissionDeniedError",
29
+ "NotFoundError",
30
+ "ConflictError",
31
+ "UnprocessableEntityError",
32
+ "RateLimitError",
33
+ "InternalServerError",
34
+ ]
@@ -0,0 +1,529 @@
1
+ # Generated by Sterling SDK Generator
2
+ # Orchestrator Control Plane API v0.1.0
3
+
4
+ import asyncio
5
+ import calendar
6
+ import email.utils
7
+ import logging
8
+ import os
9
+ import random
10
+ import time as _time
11
+ import uuid
12
+ from dataclasses import dataclass, field
13
+ from typing import Any, Optional
14
+
15
+ import httpx
16
+
17
+ from .errors import APIError, APIConnectionError
18
+ from .models import * # noqa: F403
19
+
20
+
21
+ @dataclass
22
+ class RequestOptions:
23
+ """Per-request options that override client defaults."""
24
+
25
+ #: Additional headers to send with the request.
26
+ #: Set a header to None to remove it.
27
+ headers: dict[str, str | None] | None = None
28
+
29
+ #: Request timeout in seconds. Overrides the client-level timeout.
30
+ timeout: float | None = None
31
+
32
+ logger = logging.getLogger("vers_sdk")
33
+
34
+ _LOG_LEVEL_MAP = {
35
+ "debug": logging.DEBUG,
36
+ "info": logging.INFO,
37
+ "warn": logging.WARNING,
38
+ "error": logging.ERROR,
39
+ "off": logging.CRITICAL + 1,
40
+ }
41
+
42
+
43
+ def _configure_logger() -> None:
44
+ env_level = os.environ.get("VERS_LOG", "warn").lower()
45
+ level = _LOG_LEVEL_MAP.get(env_level, logging.WARNING)
46
+ logger.setLevel(level)
47
+ if not logger.handlers:
48
+ handler = logging.StreamHandler()
49
+ handler.setFormatter(logging.Formatter("%(asctime)s [vers_sdk %(levelname)s] %(message)s"))
50
+ logger.addHandler(handler)
51
+
52
+
53
+ _configure_logger()
54
+
55
+
56
+ # Status codes that are retryable even though they are 4xx
57
+ RETRYABLE_STATUS_CODES = {408, 409, 429}
58
+
59
+
60
+ def _is_retryable_status(status: int) -> bool:
61
+ return status >= 500 or status in RETRYABLE_STATUS_CODES
62
+
63
+
64
+ def _retry_delay(attempt: int) -> float:
65
+ base = 0.5 * (2 ** attempt)
66
+ jitter = random.random() * base * 0.25
67
+ return base + jitter
68
+
69
+
70
+ def _parse_retry_after(header_value: Optional[str]) -> Optional[float]:
71
+ if header_value is None:
72
+ return None
73
+ try:
74
+ delay = float(header_value)
75
+ return min(max(delay, 0.0), 60.0)
76
+ except (ValueError, OverflowError):
77
+ pass
78
+ parsed = email.utils.parsedate(header_value)
79
+ if parsed is not None:
80
+ retry_time = calendar.timegm(parsed)
81
+ delay = retry_time - _time.time()
82
+ return min(max(delay, 0.0), 60.0)
83
+ return None
84
+
85
+
86
+ class VersSdkClient:
87
+ """Client for Orchestrator Control Plane API"""
88
+
89
+ def __init__(
90
+ self,
91
+ base_url: Optional[str] = None,
92
+ api_key: Optional[str] = None,
93
+ headers: Optional[dict[str, str | None]] = None,
94
+ max_retries: int = 2,
95
+ timeout: float = 30.0,
96
+ http_client: Optional[httpx.AsyncClient] = None,
97
+ ):
98
+ self.base_url = base_url or os.environ.get("VERS_BASE_URL", "https://api.vers.sh")
99
+ self.api_key = api_key or os.environ.get("VERS_API_KEY")
100
+ self.max_retries = max_retries
101
+ self.timeout = timeout
102
+ self._custom_headers: dict[str, str | None] = headers or {}
103
+ if http_client is not None:
104
+ self._client = http_client
105
+ else:
106
+ import platform as _platform
107
+ default_headers: dict[str, str] = {
108
+ "User-Agent": f"vers-sdk/0.1.1 python/{_platform.python_version()} {_platform.system().lower()}/{_platform.machine()}",
109
+ }
110
+ if self.api_key:
111
+ default_headers["Authorization"] = f"Bearer {self.api_key}"
112
+ merged = self._merge_headers(default_headers, self._custom_headers)
113
+ self._client = httpx.AsyncClient(
114
+ base_url=self.base_url,
115
+ headers=merged,
116
+ timeout=self.timeout,
117
+ )
118
+
119
+ @staticmethod
120
+ def _merge_headers(*sources: Optional[dict[str, str | None]]) -> dict[str, str]:
121
+ """Merge header dicts. Later sources override earlier ones (case-insensitive).
122
+ A ``None`` value removes the header."""
123
+ merged: dict[str, str] = {}
124
+ for source in sources:
125
+ if source is None:
126
+ continue
127
+ for key, value in source.items():
128
+ if value is None:
129
+ # Remove header (case-insensitive)
130
+ merged = {
131
+ k: v for k, v in merged.items()
132
+ if k.lower() != key.lower()
133
+ }
134
+ else:
135
+ merged[key] = value
136
+ return merged
137
+
138
+ def _check_response(self, response: httpx.Response) -> None:
139
+ if response.is_success:
140
+ return
141
+ body: Any = None
142
+ message: str | None = None
143
+ try:
144
+ body = response.json()
145
+ except Exception:
146
+ message = response.text
147
+ headers = dict(response.headers)
148
+ raise APIError.generate(
149
+ status=response.status_code,
150
+ body=body,
151
+ message=message,
152
+ headers=headers,
153
+ )
154
+
155
+ async def _request(self, method: str, path: str, options: RequestOptions | None = None, **kwargs: Any) -> httpx.Response:
156
+ logger.debug("request: %s %s", method, path)
157
+
158
+ req_headers: dict[str, str] = kwargs.pop("headers", {})
159
+ if method.upper() in ("POST", "PUT", "PATCH", "DELETE"):
160
+ req_headers["X-Idempotency-Key"] = str(uuid.uuid4())
161
+ # Apply per-request header overrides
162
+ if options and options.headers:
163
+ for k, v in options.headers.items():
164
+ if v is None:
165
+ req_headers.pop(k, None)
166
+ else:
167
+ req_headers[k] = v
168
+ if req_headers:
169
+ kwargs["headers"] = req_headers
170
+ effective_timeout = options.timeout if options and options.timeout is not None else None
171
+
172
+ last_exc: Optional[Exception] = None
173
+ retry_after_used = False
174
+ for attempt in range(self.max_retries + 1):
175
+ if attempt > 0 and not retry_after_used:
176
+ logger.info("retry attempt %d/%d for %s %s", attempt, self.max_retries, method, path)
177
+ await asyncio.sleep(_retry_delay(attempt - 1))
178
+ retry_after_used = False
179
+ try:
180
+ req_kwargs = dict(kwargs)
181
+ if effective_timeout is not None:
182
+ req_kwargs["timeout"] = effective_timeout
183
+ response = await self._client.request(method, path, **req_kwargs)
184
+ # Remove headers that were set to None
185
+ logger.debug("response: %d for %s %s", response.status_code, method, path)
186
+ if _is_retryable_status(response.status_code) and attempt < self.max_retries:
187
+ ra_delay = _parse_retry_after(response.headers.get("retry-after"))
188
+ if ra_delay is not None:
189
+ retry_after_used = True
190
+ logger.info("retry attempt %d/%d for %s %s (retry-after: %.1fs)", attempt + 1, self.max_retries, method, path, ra_delay)
191
+ await asyncio.sleep(ra_delay)
192
+ last_exc = Exception(f"HTTP {response.status_code}")
193
+ continue
194
+ self._check_response(response)
195
+ return response
196
+ except APIError:
197
+ raise
198
+ except (httpx.ConnectError, httpx.TimeoutException) as exc:
199
+ logger.error("connection error: %s %s - %s", method, path, exc)
200
+ if attempt == self.max_retries:
201
+ raise APIConnectionError(cause=exc) from exc
202
+ last_exc = exc
203
+ raise last_exc # type: ignore[misc]
204
+
205
+ async def resize_vm_disk(self, vm_id: str, body: dict[str, Any], params: ResizeVmDiskParams | None = None, options: RequestOptions | None = None) -> httpx.Response:
206
+ """"""
207
+ path = f"/api/v1/vm/{vm_id}/disk"
208
+ query: dict[str, Any] = {}
209
+ if params is not None:
210
+ if params.skip_wait_boot is not None:
211
+ query["skip_wait_boot"] = params.skip_wait_boot
212
+ return await self._request("PATCH", path, options, json=body, params=query)
213
+
214
+ async def exec_vm_stream_attach(self, vm_id: str, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
215
+ """"""
216
+ path = f"/api/v1/vm/{vm_id}/exec/stream/attach"
217
+ return await self._request("POST", path, options, json=body)
218
+
219
+ async def create_new_root_vm(self, body: dict[str, Any], params: CreateNewRootVmParams | None = None, options: RequestOptions | None = None) -> httpx.Response:
220
+ """"""
221
+ path = "/api/v1/vm/new_root"
222
+ query: dict[str, Any] = {}
223
+ if params is not None:
224
+ if params.wait_boot is not None:
225
+ query["wait_boot"] = params.wait_boot
226
+ return await self._request("POST", path, options, json=body, params=query)
227
+
228
+ async def vm_logs(self, vm_id: str, params: VmLogsParams | None = None, options: RequestOptions | None = None) -> httpx.Response:
229
+ """"""
230
+ path = f"/api/v1/vm/{vm_id}/logs"
231
+ query: dict[str, Any] = {}
232
+ if params is not None:
233
+ if params.offset is not None:
234
+ query["offset"] = params.offset
235
+ if params.max_entries is not None:
236
+ query["max_entries"] = params.max_entries
237
+ if params.stream is not None:
238
+ query["stream"] = params.stream
239
+ return await self._request("GET", path, options, params=query)
240
+
241
+ async def branch_by_ref(self, repo_name: str, tag_name: str, body: dict[str, Any] | None = None, params: BranchByRefParams | None = None, options: RequestOptions | None = None) -> httpx.Response:
242
+ """"""
243
+ path = f"/api/v1/vm/branch/by_ref/{repo_name}/{tag_name}"
244
+ query: dict[str, Any] = {}
245
+ if params is not None:
246
+ if params.count is not None:
247
+ query["count"] = params.count
248
+ return await self._request("POST", path, options, json=body, params=query)
249
+
250
+ async def list_public_commits(self, options: RequestOptions | None = None) -> httpx.Response:
251
+ """"""
252
+ path = "/api/v1/commits/public"
253
+ return await self._request("GET", path, options)
254
+
255
+ async def list_public_repo_tags(self, org_name: str, repo_name: str, options: RequestOptions | None = None) -> httpx.Response:
256
+ """"""
257
+ path = f"/api/v1/public/repositories/{org_name}/{repo_name}/tags"
258
+ return await self._request("GET", path, options)
259
+
260
+ async def branch_by_tag(self, tag_name: str, body: dict[str, Any] | None = None, params: BranchByTagParams | None = None, options: RequestOptions | None = None) -> httpx.Response:
261
+ """"""
262
+ path = f"/api/v1/vm/branch/by_tag/{tag_name}"
263
+ query: dict[str, Any] = {}
264
+ if params is not None:
265
+ if params.count is not None:
266
+ query["count"] = params.count
267
+ return await self._request("POST", path, options, json=body, params=query)
268
+
269
+ async def branch_vm(self, vm_or_commit_id: str, body: dict[str, Any] | None = None, params: BranchVmParams | None = None, options: RequestOptions | None = None) -> httpx.Response:
270
+ """"""
271
+ path = f"/api/v1/vm/{vm_or_commit_id}/branch"
272
+ query: dict[str, Any] = {}
273
+ if params is not None:
274
+ if params.keep_paused is not None:
275
+ query["keep_paused"] = params.keep_paused
276
+ if params.skip_wait_boot is not None:
277
+ query["skip_wait_boot"] = params.skip_wait_boot
278
+ if params.count is not None:
279
+ query["count"] = params.count
280
+ return await self._request("POST", path, options, json=body, params=query)
281
+
282
+ async def get_repo_tag(self, repo_name: str, tag_name: str, options: RequestOptions | None = None) -> httpx.Response:
283
+ """"""
284
+ path = f"/api/v1/repositories/{repo_name}/tags/{tag_name}"
285
+ return await self._request("GET", path, options)
286
+
287
+ async def delete_repo_tag(self, repo_name: str, tag_name: str, options: RequestOptions | None = None) -> httpx.Response:
288
+ """"""
289
+ path = f"/api/v1/repositories/{repo_name}/tags/{tag_name}"
290
+ return await self._request("DELETE", path, options)
291
+
292
+ async def update_repo_tag(self, repo_name: str, tag_name: str, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
293
+ """"""
294
+ path = f"/api/v1/repositories/{repo_name}/tags/{tag_name}"
295
+ return await self._request("PATCH", path, options, json=body)
296
+
297
+ async def get_repository(self, repo_name: str, options: RequestOptions | None = None) -> httpx.Response:
298
+ """"""
299
+ path = f"/api/v1/repositories/{repo_name}"
300
+ return await self._request("GET", path, options)
301
+
302
+ async def delete_repository(self, repo_name: str, options: RequestOptions | None = None) -> httpx.Response:
303
+ """"""
304
+ path = f"/api/v1/repositories/{repo_name}"
305
+ return await self._request("DELETE", path, options)
306
+
307
+ async def list_parent_commits(self, commit_id: str, options: RequestOptions | None = None) -> httpx.Response:
308
+ """"""
309
+ path = f"/api/v1/vm/commits/{commit_id}/parents"
310
+ return await self._request("GET", path, options)
311
+
312
+ async def update_vm_state(self, vm_id: str, body: dict[str, Any], params: UpdateVmStateParams | None = None, options: RequestOptions | None = None) -> httpx.Response:
313
+ """"""
314
+ path = f"/api/v1/vm/{vm_id}/state"
315
+ query: dict[str, Any] = {}
316
+ if params is not None:
317
+ if params.skip_wait_boot is not None:
318
+ query["skip_wait_boot"] = params.skip_wait_boot
319
+ return await self._request("PATCH", path, options, json=body, params=query)
320
+
321
+ async def fork_repository(self, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
322
+ """"""
323
+ path = "/api/v1/repositories/fork"
324
+ return await self._request("POST", path, options, json=body)
325
+
326
+ async def list_repositories(self, options: RequestOptions | None = None) -> httpx.Response:
327
+ """"""
328
+ path = "/api/v1/repositories"
329
+ return await self._request("GET", path, options)
330
+
331
+ async def create_repository(self, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
332
+ """"""
333
+ path = "/api/v1/repositories"
334
+ return await self._request("POST", path, options, json=body)
335
+
336
+ async def restore_from_commit(self, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
337
+ """"""
338
+ path = "/api/v1/vm/from_commit"
339
+ return await self._request("POST", path, options, json=body)
340
+
341
+ async def get_domain(self, domain_id: str, options: RequestOptions | None = None) -> httpx.Response:
342
+ """"""
343
+ path = f"/api/v1/domains/{domain_id}"
344
+ return await self._request("GET", path, options)
345
+
346
+ async def delete_domain(self, domain_id: str, options: RequestOptions | None = None) -> httpx.Response:
347
+ """"""
348
+ path = f"/api/v1/domains/{domain_id}"
349
+ return await self._request("DELETE", path, options)
350
+
351
+ async def list_tags(self, options: RequestOptions | None = None) -> httpx.Response:
352
+ """"""
353
+ path = "/api/v1/commit_tags"
354
+ return await self._request("GET", path, options)
355
+
356
+ async def create_tag(self, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
357
+ """"""
358
+ path = "/api/v1/commit_tags"
359
+ return await self._request("POST", path, options, json=body)
360
+
361
+ async def list_public_repositories(self, options: RequestOptions | None = None) -> httpx.Response:
362
+ """"""
363
+ path = "/api/v1/public/repositories"
364
+ return await self._request("GET", path, options)
365
+
366
+ async def get_public_repo_tag(self, org_name: str, repo_name: str, tag_name: str, options: RequestOptions | None = None) -> httpx.Response:
367
+ """"""
368
+ path = f"/api/v1/public/repositories/{org_name}/{repo_name}/tags/{tag_name}"
369
+ return await self._request("GET", path, options)
370
+
371
+ async def get_vm_metadata(self, vm_id: str, options: RequestOptions | None = None) -> httpx.Response:
372
+ """"""
373
+ path = f"/api/v1/vm/{vm_id}/metadata"
374
+ return await self._request("GET", path, options)
375
+
376
+ async def list_env_vars(self, options: RequestOptions | None = None) -> httpx.Response:
377
+ """"""
378
+ path = "/api/v1/env_vars"
379
+ return await self._request("GET", path, options)
380
+
381
+ async def set_env_vars(self, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
382
+ """"""
383
+ path = "/api/v1/env_vars"
384
+ return await self._request("PUT", path, options, json=body)
385
+
386
+ async def delete_vm(self, vm_id: str, params: DeleteVmParams | None = None, options: RequestOptions | None = None) -> httpx.Response:
387
+ """"""
388
+ path = f"/api/v1/vm/{vm_id}"
389
+ query: dict[str, Any] = {}
390
+ if params is not None:
391
+ if params.skip_wait_boot is not None:
392
+ query["skip_wait_boot"] = params.skip_wait_boot
393
+ return await self._request("DELETE", path, options, params=query)
394
+
395
+ async def list_commits(self, options: RequestOptions | None = None) -> httpx.Response:
396
+ """"""
397
+ path = "/api/v1/commits"
398
+ return await self._request("GET", path, options)
399
+
400
+ async def get_tag(self, tag_name: str, options: RequestOptions | None = None) -> httpx.Response:
401
+ """"""
402
+ path = f"/api/v1/commit_tags/{tag_name}"
403
+ return await self._request("GET", path, options)
404
+
405
+ async def delete_tag(self, tag_name: str, options: RequestOptions | None = None) -> httpx.Response:
406
+ """"""
407
+ path = f"/api/v1/commit_tags/{tag_name}"
408
+ return await self._request("DELETE", path, options)
409
+
410
+ async def update_tag(self, tag_name: str, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
411
+ """"""
412
+ path = f"/api/v1/commit_tags/{tag_name}"
413
+ return await self._request("PATCH", path, options, json=body)
414
+
415
+ async def branch_by_vm(self, vm_id: str, body: dict[str, Any] | None = None, params: BranchByVmParams | None = None, options: RequestOptions | None = None) -> httpx.Response:
416
+ """"""
417
+ path = f"/api/v1/vm/branch/by_vm/{vm_id}"
418
+ query: dict[str, Any] = {}
419
+ if params is not None:
420
+ if params.keep_paused is not None:
421
+ query["keep_paused"] = params.keep_paused
422
+ if params.skip_wait_boot is not None:
423
+ query["skip_wait_boot"] = params.skip_wait_boot
424
+ if params.count is not None:
425
+ query["count"] = params.count
426
+ return await self._request("POST", path, options, json=body, params=query)
427
+
428
+ async def delete_env_var(self, key: str, options: RequestOptions | None = None) -> httpx.Response:
429
+ """"""
430
+ path = f"/api/v1/env_vars/{key}"
431
+ return await self._request("DELETE", path, options)
432
+
433
+ async def exec_vm_stream(self, vm_id: str, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
434
+ """"""
435
+ path = f"/api/v1/vm/{vm_id}/exec/stream"
436
+ return await self._request("POST", path, options, json=body)
437
+
438
+ async def commit_vm(self, vm_id: str, body: dict[str, Any] | None = None, params: CommitVmParams | None = None, options: RequestOptions | None = None) -> httpx.Response:
439
+ """"""
440
+ path = f"/api/v1/vm/{vm_id}/commit"
441
+ query: dict[str, Any] = {}
442
+ if params is not None:
443
+ if params.keep_paused is not None:
444
+ query["keep_paused"] = params.keep_paused
445
+ if params.skip_wait_boot is not None:
446
+ query["skip_wait_boot"] = params.skip_wait_boot
447
+ return await self._request("POST", path, options, json=body, params=query)
448
+
449
+ async def delete_commit(self, commit_id: str, options: RequestOptions | None = None) -> httpx.Response:
450
+ """"""
451
+ path = f"/api/v1/commits/{commit_id}"
452
+ return await self._request("DELETE", path, options)
453
+
454
+ async def update_commit(self, commit_id: str, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
455
+ """"""
456
+ path = f"/api/v1/commits/{commit_id}"
457
+ return await self._request("PATCH", path, options, json=body)
458
+
459
+ async def list_repo_tags(self, repo_name: str, options: RequestOptions | None = None) -> httpx.Response:
460
+ """"""
461
+ path = f"/api/v1/repositories/{repo_name}/tags"
462
+ return await self._request("GET", path, options)
463
+
464
+ async def create_repo_tag(self, repo_name: str, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
465
+ """"""
466
+ path = f"/api/v1/repositories/{repo_name}/tags"
467
+ return await self._request("POST", path, options, json=body)
468
+
469
+ async def ssh_key(self, vm_id: str, options: RequestOptions | None = None) -> httpx.Response:
470
+ """"""
471
+ path = f"/api/v1/vm/{vm_id}/ssh_key"
472
+ return await self._request("GET", path, options)
473
+
474
+ async def exec_vm(self, vm_id: str, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
475
+ """"""
476
+ path = f"/api/v1/vm/{vm_id}/exec"
477
+ return await self._request("POST", path, options, json=body)
478
+
479
+ async def set_repository_visibility(self, repo_name: str, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
480
+ """"""
481
+ path = f"/api/v1/repositories/{repo_name}/visibility"
482
+ return await self._request("PATCH", path, options, json=body)
483
+
484
+ async def list_vms(self, options: RequestOptions | None = None) -> httpx.Response:
485
+ """"""
486
+ path = "/api/v1/vms"
487
+ return await self._request("GET", path, options)
488
+
489
+ async def list_domains(self, params: ListDomainsParams | None = None, options: RequestOptions | None = None) -> httpx.Response:
490
+ """"""
491
+ path = "/api/v1/domains"
492
+ query: dict[str, Any] = {}
493
+ if params is not None:
494
+ if params.vm_id is not None:
495
+ query["vm_id"] = params.vm_id
496
+ return await self._request("GET", path, options, params=query)
497
+
498
+ async def create_domain(self, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
499
+ """"""
500
+ path = "/api/v1/domains"
501
+ return await self._request("POST", path, options, json=body)
502
+
503
+ async def vm_status(self, vm_id: str, options: RequestOptions | None = None) -> httpx.Response:
504
+ """"""
505
+ path = f"/api/v1/vm/{vm_id}/status"
506
+ return await self._request("GET", path, options)
507
+
508
+ async def get_public_repository(self, org_name: str, repo_name: str, options: RequestOptions | None = None) -> httpx.Response:
509
+ """"""
510
+ path = f"/api/v1/public/repositories/{org_name}/{repo_name}"
511
+ return await self._request("GET", path, options)
512
+
513
+ async def branch_by_commit(self, commit_id: str, body: dict[str, Any] | None = None, params: BranchByCommitParams | None = None, options: RequestOptions | None = None) -> httpx.Response:
514
+ """"""
515
+ path = f"/api/v1/vm/branch/by_commit/{commit_id}"
516
+ query: dict[str, Any] = {}
517
+ if params is not None:
518
+ if params.count is not None:
519
+ query["count"] = params.count
520
+ return await self._request("POST", path, options, json=body, params=query)
521
+
522
+ async def close(self) -> None:
523
+ await self._client.aclose()
524
+
525
+ async def __aenter__(self) -> "VersSdkClient":
526
+ return self
527
+
528
+ async def __aexit__(self, *args: Any) -> None:
529
+ await self.close()