blaxel 0.1.9rc36__py3-none-any.whl → 0.1.10__py3-none-any.whl

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.
Files changed (83) hide show
  1. blaxel/agents/__init__.py +52 -15
  2. blaxel/authentication/__init__.py +11 -2
  3. blaxel/client/api/compute/create_sandbox_preview.py +179 -0
  4. blaxel/client/api/compute/create_sandbox_preview_token.py +192 -0
  5. blaxel/client/api/compute/delete_sandbox_preview.py +167 -0
  6. blaxel/client/api/compute/delete_sandbox_preview_token.py +180 -0
  7. blaxel/client/api/compute/get_sandbox_preview.py +167 -0
  8. blaxel/client/api/compute/list_sandbox_preview_tokens.py +172 -0
  9. blaxel/client/api/compute/list_sandbox_previews.py +159 -0
  10. blaxel/client/api/compute/update_sandbox_preview.py +192 -0
  11. blaxel/client/api/integrations/get_integration.py +64 -7
  12. blaxel/client/api/workspaces/check_workspace_availability.py +165 -0
  13. blaxel/client/models/__init__.py +32 -2
  14. blaxel/client/models/check_workspace_availability_body.py +60 -0
  15. blaxel/client/models/delete_sandbox_preview_token_response_200.py +60 -0
  16. blaxel/client/models/integration.py +197 -0
  17. blaxel/client/models/integration_additional_infos.py +45 -0
  18. blaxel/client/models/integration_endpoint.py +143 -0
  19. blaxel/client/models/integration_endpoint_token.py +79 -0
  20. blaxel/client/models/integration_endpoints.py +61 -0
  21. blaxel/client/models/integration_headers.py +45 -0
  22. blaxel/client/models/integration_organization.py +88 -0
  23. blaxel/client/models/integration_query_params.py +45 -0
  24. blaxel/client/models/metrics.py +9 -0
  25. blaxel/client/models/preview.py +96 -0
  26. blaxel/client/models/preview_metadata.py +133 -0
  27. blaxel/client/models/preview_spec.py +79 -0
  28. blaxel/client/models/preview_token.py +96 -0
  29. blaxel/client/models/preview_token_metadata.py +97 -0
  30. blaxel/client/models/preview_token_spec.py +88 -0
  31. blaxel/common/autoload.py +0 -2
  32. blaxel/common/internal.py +75 -0
  33. blaxel/common/settings.py +6 -1
  34. blaxel/mcp/server.py +2 -1
  35. blaxel/sandbox/base.py +68 -0
  36. blaxel/sandbox/client/__init__.py +8 -0
  37. blaxel/sandbox/client/api/__init__.py +1 -0
  38. blaxel/sandbox/client/api/filesystem/__init__.py +0 -0
  39. blaxel/sandbox/client/api/filesystem/delete_filesystem_path.py +184 -0
  40. blaxel/sandbox/client/api/filesystem/get_filesystem_path.py +184 -0
  41. blaxel/sandbox/client/api/filesystem/put_filesystem_path.py +189 -0
  42. blaxel/sandbox/client/api/network/__init__.py +0 -0
  43. blaxel/sandbox/client/api/network/delete_network_process_pid_monitor.py +169 -0
  44. blaxel/sandbox/client/api/network/get_network_process_pid_ports.py +169 -0
  45. blaxel/sandbox/client/api/network/post_network_process_pid_monitor.py +195 -0
  46. blaxel/sandbox/client/api/process/__init__.py +0 -0
  47. blaxel/sandbox/client/api/process/delete_process_identifier.py +163 -0
  48. blaxel/sandbox/client/api/process/delete_process_identifier_kill.py +189 -0
  49. blaxel/sandbox/client/api/process/get_process.py +135 -0
  50. blaxel/sandbox/client/api/process/get_process_identifier.py +159 -0
  51. blaxel/sandbox/client/api/process/get_process_identifier_logs.py +188 -0
  52. blaxel/sandbox/client/api/process/get_process_identifier_logs_stream.py +190 -0
  53. blaxel/sandbox/client/api/process/post_process.py +176 -0
  54. blaxel/sandbox/client/client.py +162 -0
  55. blaxel/sandbox/client/errors.py +16 -0
  56. blaxel/sandbox/client/models/__init__.py +41 -0
  57. blaxel/sandbox/client/models/delete_network_process_pid_monitor_response_200.py +45 -0
  58. blaxel/sandbox/client/models/directory.py +112 -0
  59. blaxel/sandbox/client/models/error_response.py +60 -0
  60. blaxel/sandbox/client/models/file.py +105 -0
  61. blaxel/sandbox/client/models/file_request.py +78 -0
  62. blaxel/sandbox/client/models/file_with_content.py +114 -0
  63. blaxel/sandbox/client/models/get_network_process_pid_ports_response_200.py +45 -0
  64. blaxel/sandbox/client/models/get_process_identifier_logs_response_200.py +45 -0
  65. blaxel/sandbox/client/models/get_process_identifier_logs_stream_response_200.py +45 -0
  66. blaxel/sandbox/client/models/port_monitor_request.py +60 -0
  67. blaxel/sandbox/client/models/post_network_process_pid_monitor_response_200.py +45 -0
  68. blaxel/sandbox/client/models/process_kill_request.py +60 -0
  69. blaxel/sandbox/client/models/process_request.py +118 -0
  70. blaxel/sandbox/client/models/process_response.py +123 -0
  71. blaxel/sandbox/client/models/subdirectory.py +60 -0
  72. blaxel/sandbox/client/models/success_response.py +69 -0
  73. blaxel/sandbox/client/py.typed +1 -0
  74. blaxel/sandbox/client/types.py +46 -0
  75. blaxel/sandbox/filesystem.py +104 -0
  76. blaxel/sandbox/process.py +57 -0
  77. blaxel/sandbox/sandbox.py +92 -0
  78. blaxel/tools/__init__.py +62 -21
  79. {blaxel-0.1.9rc36.dist-info → blaxel-0.1.10.dist-info}/METADATA +1 -1
  80. {blaxel-0.1.9rc36.dist-info → blaxel-0.1.10.dist-info}/RECORD +82 -14
  81. blaxel/client/models/sandboxes.py +0 -129
  82. {blaxel-0.1.9rc36.dist-info → blaxel-0.1.10.dist-info}/WHEEL +0 -0
  83. {blaxel-0.1.9rc36.dist-info → blaxel-0.1.10.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,75 @@
1
+ import base64
2
+ import hashlib
3
+ import os
4
+ import re
5
+ from logging import getLogger
6
+ from typing import Optional
7
+
8
+ logger = getLogger(__name__)
9
+
10
+ def get_alphanumeric_limited_hash(input_str, max_size):
11
+ # Create SHA-256 hash of the input string
12
+ hash_obj = hashlib.sha256(input_str.encode('utf-8'))
13
+
14
+ # Get the hash digest in base64 format
15
+ hash_base64 = base64.b64encode(hash_obj.digest()).decode('utf-8')
16
+
17
+ # Remove non-alphanumeric characters and convert to lowercase
18
+ alphanumeric = re.sub(r'[^a-zA-Z0-9]', '', hash_base64).lower()
19
+
20
+ # Skip the first character to match the Node.js crypto output
21
+ alphanumeric = alphanumeric[1:]
22
+
23
+ # Limit to max_size characters
24
+ return alphanumeric[:max_size] if len(alphanumeric) > max_size else alphanumeric
25
+
26
+
27
+ def get_global_unique_hash(workspace: str, type: str, name: str) -> str:
28
+ """
29
+ Generate a unique hash for a combination of workspace, type, and name.
30
+
31
+ Args:
32
+ workspace: The workspace identifier
33
+ type: The type identifier
34
+ name: The name identifier
35
+
36
+ Returns:
37
+ A unique alphanumeric hash string of maximum length 48
38
+ """
39
+ global_unique_name = f"{workspace}-{type}-{name}"
40
+ hash = get_alphanumeric_limited_hash(global_unique_name, 48)
41
+ return hash
42
+
43
+ class Agent:
44
+ def __init__(self, agent_name: str, workspace: str, run_internal_protocol: str, run_internal_hostname: str):
45
+ self.agent_name = agent_name
46
+ self.workspace = workspace
47
+ self.run_internal_protocol = run_internal_protocol
48
+ self.run_internal_hostname = run_internal_hostname
49
+
50
+ @property
51
+ def internal_url(self) -> str:
52
+ """
53
+ Generate the internal URL for the agent using a unique hash.
54
+
55
+ Returns:
56
+ The internal URL as a string
57
+ """
58
+ hash_value = get_global_unique_hash(
59
+ self.workspace,
60
+ "agent",
61
+ self.agent_name
62
+ )
63
+ return f"{self.run_internal_protocol}://{hash_value}.{self.run_internal_hostname}"
64
+
65
+ @property
66
+ def forced_url(self) -> Optional[str]:
67
+ """
68
+ Check for a forced URL in environment variables.
69
+
70
+ Returns:
71
+ The forced URL if found in environment variables, None otherwise
72
+ """
73
+ env_var = self.agent_name.replace("-", "_").upper()
74
+ env_key = f"BL_AGENT_{env_var}_URL"
75
+ return os.environ.get(env_key)
blaxel/common/settings.py CHANGED
@@ -21,7 +21,7 @@ class Settings:
21
21
  @property
22
22
  def log_level(self) -> str:
23
23
  """Get the log level."""
24
- return os.environ.get("LOG_LEVEL", "INFO")
24
+ return os.environ.get("LOG_LEVEL", "INFO").upper()
25
25
 
26
26
  @property
27
27
  def base_url(self) -> str:
@@ -69,6 +69,11 @@ class Settings:
69
69
  """Is running on bl cloud."""
70
70
  return os.environ.get("BL_CLOUD", "") == "true"
71
71
 
72
+ @property
73
+ def run_internal_protocol(self) -> str:
74
+ """Get the run internal protocol."""
75
+ return os.environ.get("BL_RUN_INTERNAL_PROTOCOL", "https")
76
+
72
77
  @property
73
78
  def enable_opentelemetry(self) -> bool:
74
79
  """Get the enable opentelemetry."""
blaxel/mcp/server.py CHANGED
@@ -65,7 +65,8 @@ class BlaxelMcpServerTransport:
65
65
  "mcp.message.parsed": True,
66
66
  "mcp.method": getattr(msg, "method", None),
67
67
  "mcp.messageId": getattr(msg, "id", None),
68
- "mcp.toolName": getattr(getattr(msg, "params", None), "name", None)
68
+ "mcp.toolName": getattr(getattr(msg, "params", None), "name", None),
69
+ "span.type": "mcp.message",
69
70
  })
70
71
  self.spans[client_id+":"+msg.id] = span
71
72
  await read_stream_writer.send(msg)
blaxel/sandbox/base.py ADDED
@@ -0,0 +1,68 @@
1
+ import os
2
+
3
+ from httpx import Response
4
+
5
+ from ..client.models import Sandbox
6
+ from ..common.internal import get_global_unique_hash
7
+ from ..common.settings import settings
8
+ from .client.client import client
9
+ from .client.models import ErrorResponse
10
+
11
+
12
+ class ResponseError(Exception):
13
+ def __init__(self, response: Response):
14
+ self.status_code = response.status_code
15
+ self.status_text = response.content
16
+ self.error = None
17
+ data_error = {
18
+ "status": response.status_code,
19
+ "statusText": response.content,
20
+ }
21
+ if hasattr(response, "parsed") and isinstance(response.parsed, ErrorResponse):
22
+ data_error["error"] = response.parsed.error
23
+ self.error = response.parsed.error
24
+ super().__init__(str(data_error))
25
+
26
+
27
+ class SandboxHandleBase:
28
+ def __init__(self, sandbox: Sandbox):
29
+ self.sandbox = sandbox
30
+ self.client = client.with_base_url(self.url).with_headers(settings.headers)
31
+
32
+ @property
33
+ def name(self):
34
+ return self.sandbox.metadata and self.sandbox.metadata.name
35
+
36
+ @property
37
+ def fallback_url(self):
38
+ if self.external_url != self.url:
39
+ return self.external_url
40
+ return None
41
+
42
+ @property
43
+ def external_url(self):
44
+ return f"{settings.run_url}/{settings.workspace}/sandboxes/{self.name}"
45
+
46
+ @property
47
+ def internal_url(self):
48
+ hash_ = get_global_unique_hash(settings.workspace, "sandbox", self.name)
49
+ return f"{settings.run_internal_protocol}://bl-{settings.env}-{hash_}.{settings.run_internal_hostname}"
50
+
51
+ @property
52
+ def forced_url(self):
53
+ env_var = self.name.replace("-", "_").upper()
54
+ env_name = f"BL_SANDBOX_{env_var}_URL"
55
+ return os.environ.get(env_name)
56
+
57
+ @property
58
+ def url(self):
59
+ if self.forced_url:
60
+ return self.forced_url
61
+ if settings.run_internal_hostname:
62
+ return self.internal_url
63
+ return self.external_url
64
+
65
+ def handle_response(self, response: Response):
66
+ if response.status_code >= 400:
67
+ raise ResponseError(response)
68
+
@@ -0,0 +1,8 @@
1
+ """A client library for accessing Sandbox API"""
2
+
3
+ from .client import Client, client
4
+
5
+ __all__ = (
6
+ "Client",
7
+ "client",
8
+ )
@@ -0,0 +1 @@
1
+ """Contains methods for accessing the API"""
File without changes
@@ -0,0 +1,184 @@
1
+ from http import HTTPStatus
2
+ from typing import Any, Optional, Union
3
+
4
+ import httpx
5
+
6
+ from ... import errors
7
+ from ...client import Client
8
+ from ...models.error_response import ErrorResponse
9
+ from ...models.success_response import SuccessResponse
10
+ from ...types import UNSET, Response, Unset
11
+
12
+
13
+ def _get_kwargs(
14
+ path: str,
15
+ *,
16
+ recursive: Union[Unset, bool] = UNSET,
17
+ ) -> dict[str, Any]:
18
+ params: dict[str, Any] = {}
19
+
20
+ params["recursive"] = recursive
21
+
22
+ params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
23
+
24
+ _kwargs: dict[str, Any] = {
25
+ "method": "delete",
26
+ "url": f"/filesystem/{path}",
27
+ "params": params,
28
+ }
29
+
30
+ return _kwargs
31
+
32
+
33
+ def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Union[ErrorResponse, SuccessResponse]]:
34
+ if response.status_code == 200:
35
+ response_200 = SuccessResponse.from_dict(response.json())
36
+
37
+ return response_200
38
+ if response.status_code == 404:
39
+ response_404 = ErrorResponse.from_dict(response.json())
40
+
41
+ return response_404
42
+ if response.status_code == 500:
43
+ response_500 = ErrorResponse.from_dict(response.json())
44
+
45
+ return response_500
46
+ if client.raise_on_unexpected_status:
47
+ raise errors.UnexpectedStatus(response.status_code, response.content)
48
+ else:
49
+ return None
50
+
51
+
52
+ def _build_response(*, client: Client, response: httpx.Response) -> Response[Union[ErrorResponse, SuccessResponse]]:
53
+ return Response(
54
+ status_code=HTTPStatus(response.status_code),
55
+ content=response.content,
56
+ headers=response.headers,
57
+ parsed=_parse_response(client=client, response=response),
58
+ )
59
+
60
+
61
+ def sync_detailed(
62
+ path: str,
63
+ *,
64
+ client: Union[Client],
65
+ recursive: Union[Unset, bool] = UNSET,
66
+ ) -> Response[Union[ErrorResponse, SuccessResponse]]:
67
+ """Delete file or directory
68
+
69
+ Delete a file or directory
70
+
71
+ Args:
72
+ path (str):
73
+ recursive (Union[Unset, bool]):
74
+
75
+ Raises:
76
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
77
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
78
+
79
+ Returns:
80
+ Response[Union[ErrorResponse, SuccessResponse]]
81
+ """
82
+
83
+ kwargs = _get_kwargs(
84
+ path=path,
85
+ recursive=recursive,
86
+ )
87
+
88
+ response = client.get_httpx_client().request(
89
+ **kwargs,
90
+ )
91
+
92
+ return _build_response(client=client, response=response)
93
+
94
+
95
+ def sync(
96
+ path: str,
97
+ *,
98
+ client: Union[Client],
99
+ recursive: Union[Unset, bool] = UNSET,
100
+ ) -> Optional[Union[ErrorResponse, SuccessResponse]]:
101
+ """Delete file or directory
102
+
103
+ Delete a file or directory
104
+
105
+ Args:
106
+ path (str):
107
+ recursive (Union[Unset, bool]):
108
+
109
+ Raises:
110
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
111
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
112
+
113
+ Returns:
114
+ Union[ErrorResponse, SuccessResponse]
115
+ """
116
+
117
+ return sync_detailed(
118
+ path=path,
119
+ client=client,
120
+ recursive=recursive,
121
+ ).parsed
122
+
123
+
124
+ async def asyncio_detailed(
125
+ path: str,
126
+ *,
127
+ client: Union[Client],
128
+ recursive: Union[Unset, bool] = UNSET,
129
+ ) -> Response[Union[ErrorResponse, SuccessResponse]]:
130
+ """Delete file or directory
131
+
132
+ Delete a file or directory
133
+
134
+ Args:
135
+ path (str):
136
+ recursive (Union[Unset, bool]):
137
+
138
+ Raises:
139
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
140
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
141
+
142
+ Returns:
143
+ Response[Union[ErrorResponse, SuccessResponse]]
144
+ """
145
+
146
+ kwargs = _get_kwargs(
147
+ path=path,
148
+ recursive=recursive,
149
+ )
150
+
151
+ response = await client.get_async_httpx_client().request(**kwargs)
152
+
153
+ return _build_response(client=client, response=response)
154
+
155
+
156
+ async def asyncio(
157
+ path: str,
158
+ *,
159
+ client: Union[Client],
160
+ recursive: Union[Unset, bool] = UNSET,
161
+ ) -> Optional[Union[ErrorResponse, SuccessResponse]]:
162
+ """Delete file or directory
163
+
164
+ Delete a file or directory
165
+
166
+ Args:
167
+ path (str):
168
+ recursive (Union[Unset, bool]):
169
+
170
+ Raises:
171
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
172
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
173
+
174
+ Returns:
175
+ Union[ErrorResponse, SuccessResponse]
176
+ """
177
+
178
+ return (
179
+ await asyncio_detailed(
180
+ path=path,
181
+ client=client,
182
+ recursive=recursive,
183
+ )
184
+ ).parsed
@@ -0,0 +1,184 @@
1
+ from http import HTTPStatus
2
+ from typing import Any, Optional, Union
3
+
4
+ import httpx
5
+
6
+ from ... import errors
7
+ from ...client import Client
8
+ from ...models.directory import Directory
9
+ from ...models.error_response import ErrorResponse
10
+ from ...models.file_with_content import FileWithContent
11
+ from ...types import Response
12
+
13
+
14
+ def _get_kwargs(
15
+ path: str,
16
+ ) -> dict[str, Any]:
17
+ _kwargs: dict[str, Any] = {
18
+ "method": "get",
19
+ "url": f"/filesystem/{path}",
20
+ }
21
+
22
+ return _kwargs
23
+
24
+
25
+ def _parse_response(
26
+ *, client: Client, response: httpx.Response
27
+ ) -> Optional[Union[ErrorResponse, Union["Directory", "FileWithContent"]]]:
28
+ if response.status_code == 200:
29
+
30
+ def _parse_response_200(data: object) -> Union["Directory", "FileWithContent"]:
31
+ try:
32
+ if not isinstance(data, dict):
33
+ raise TypeError()
34
+ response_200_type_0 = Directory.from_dict(data)
35
+
36
+ return response_200_type_0
37
+ except: # noqa: E722
38
+ pass
39
+ if not isinstance(data, dict):
40
+ raise TypeError()
41
+ response_200_type_1 = FileWithContent.from_dict(data)
42
+
43
+ return response_200_type_1
44
+
45
+ response_200 = _parse_response_200(response.json())
46
+
47
+ return response_200
48
+ if response.status_code == 404:
49
+ response_404 = ErrorResponse.from_dict(response.json())
50
+
51
+ return response_404
52
+ if response.status_code == 500:
53
+ response_500 = ErrorResponse.from_dict(response.json())
54
+
55
+ return response_500
56
+ if client.raise_on_unexpected_status:
57
+ raise errors.UnexpectedStatus(response.status_code, response.content)
58
+ else:
59
+ return None
60
+
61
+
62
+ def _build_response(
63
+ *, client: Client, response: httpx.Response
64
+ ) -> Response[Union[ErrorResponse, Union["Directory", "FileWithContent"]]]:
65
+ return Response(
66
+ status_code=HTTPStatus(response.status_code),
67
+ content=response.content,
68
+ headers=response.headers,
69
+ parsed=_parse_response(client=client, response=response),
70
+ )
71
+
72
+
73
+ def sync_detailed(
74
+ path: str,
75
+ *,
76
+ client: Union[Client],
77
+ ) -> Response[Union[ErrorResponse, Union["Directory", "FileWithContent"]]]:
78
+ """Get file or directory information
79
+
80
+ Get content of a file or listing of a directory
81
+
82
+ Args:
83
+ path (str):
84
+
85
+ Raises:
86
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
87
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
88
+
89
+ Returns:
90
+ Response[Union[ErrorResponse, Union['Directory', 'FileWithContent']]]
91
+ """
92
+
93
+ kwargs = _get_kwargs(
94
+ path=path,
95
+ )
96
+
97
+ response = client.get_httpx_client().request(
98
+ **kwargs,
99
+ )
100
+
101
+ return _build_response(client=client, response=response)
102
+
103
+
104
+ def sync(
105
+ path: str,
106
+ *,
107
+ client: Union[Client],
108
+ ) -> Optional[Union[ErrorResponse, Union["Directory", "FileWithContent"]]]:
109
+ """Get file or directory information
110
+
111
+ Get content of a file or listing of a directory
112
+
113
+ Args:
114
+ path (str):
115
+
116
+ Raises:
117
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
118
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
119
+
120
+ Returns:
121
+ Union[ErrorResponse, Union['Directory', 'FileWithContent']]
122
+ """
123
+
124
+ return sync_detailed(
125
+ path=path,
126
+ client=client,
127
+ ).parsed
128
+
129
+
130
+ async def asyncio_detailed(
131
+ path: str,
132
+ *,
133
+ client: Union[Client],
134
+ ) -> Response[Union[ErrorResponse, Union["Directory", "FileWithContent"]]]:
135
+ """Get file or directory information
136
+
137
+ Get content of a file or listing of a directory
138
+
139
+ Args:
140
+ path (str):
141
+
142
+ Raises:
143
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
144
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
145
+
146
+ Returns:
147
+ Response[Union[ErrorResponse, Union['Directory', 'FileWithContent']]]
148
+ """
149
+
150
+ kwargs = _get_kwargs(
151
+ path=path,
152
+ )
153
+
154
+ response = await client.get_async_httpx_client().request(**kwargs)
155
+
156
+ return _build_response(client=client, response=response)
157
+
158
+
159
+ async def asyncio(
160
+ path: str,
161
+ *,
162
+ client: Union[Client],
163
+ ) -> Optional[Union[ErrorResponse, Union["Directory", "FileWithContent"]]]:
164
+ """Get file or directory information
165
+
166
+ Get content of a file or listing of a directory
167
+
168
+ Args:
169
+ path (str):
170
+
171
+ Raises:
172
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
173
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
174
+
175
+ Returns:
176
+ Union[ErrorResponse, Union['Directory', 'FileWithContent']]
177
+ """
178
+
179
+ return (
180
+ await asyncio_detailed(
181
+ path=path,
182
+ client=client,
183
+ )
184
+ ).parsed