waveium 0.2.0__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.
- waveium/__init__.py +204 -0
- waveium/_config.py +88 -0
- waveium/_http.py +362 -0
- waveium/_sse.py +497 -0
- waveium/async_client.py +92 -0
- waveium/client.py +90 -0
- waveium/exceptions.py +131 -0
- waveium/models/__init__.py +224 -0
- waveium/models/auth.py +58 -0
- waveium/models/common.py +130 -0
- waveium/models/environments.py +247 -0
- waveium/models/events.py +132 -0
- waveium/models/executions.py +145 -0
- waveium/models/files.py +158 -0
- waveium/models/organizations.py +51 -0
- waveium/models/projects.py +177 -0
- waveium/models/roles.py +48 -0
- waveium/models/scenes.py +222 -0
- waveium/models/shares.py +51 -0
- waveium/models/teams.py +90 -0
- waveium/models/users.py +88 -0
- waveium/resources/__init__.py +41 -0
- waveium/resources/_base.py +30 -0
- waveium/resources/_file_ops.py +371 -0
- waveium/resources/_share_ops.py +175 -0
- waveium/resources/_stream_ops.py +318 -0
- waveium/resources/_upload_ops.py +286 -0
- waveium/resources/auth.py +157 -0
- waveium/resources/environments.py +1047 -0
- waveium/resources/executions.py +1142 -0
- waveium/resources/organizations.py +281 -0
- waveium/resources/projects.py +559 -0
- waveium/resources/roles.py +104 -0
- waveium/resources/scenes.py +1314 -0
- waveium/resources/teams.py +351 -0
- waveium/resources/users.py +448 -0
- waveium-0.2.0.dist-info/METADATA +426 -0
- waveium-0.2.0.dist-info/RECORD +40 -0
- waveium-0.2.0.dist-info/WHEEL +4 -0
- waveium-0.2.0.dist-info/licenses/LICENSE +21 -0
waveium/exceptions.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""Exceptions for the Waveium SDK."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, Optional
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class WaveiumError(Exception):
|
|
7
|
+
"""Base exception for all Waveium SDK errors."""
|
|
8
|
+
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
message: str,
|
|
12
|
+
code: Optional[str] = None,
|
|
13
|
+
details: Optional[Dict[str, Any]] = None,
|
|
14
|
+
) -> None:
|
|
15
|
+
"""Initialize WaveiumError.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
message: Error message
|
|
19
|
+
code: Optional error code from API
|
|
20
|
+
details: Optional additional error details
|
|
21
|
+
"""
|
|
22
|
+
super().__init__(message)
|
|
23
|
+
self.message = message
|
|
24
|
+
self.code = code
|
|
25
|
+
self.details = details or {}
|
|
26
|
+
|
|
27
|
+
def __str__(self) -> str:
|
|
28
|
+
"""Return string representation of error."""
|
|
29
|
+
if self.code:
|
|
30
|
+
return f"[{self.code}] {self.message}"
|
|
31
|
+
return self.message
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class AuthenticationError(WaveiumError):
|
|
35
|
+
"""Raised when authentication fails (401)."""
|
|
36
|
+
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class AuthorizationError(WaveiumError):
|
|
41
|
+
"""Raised when user lacks permission for operation (403)."""
|
|
42
|
+
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ConflictError(WaveiumError):
|
|
47
|
+
"""Raised when operation conflicts with resource state (409)."""
|
|
48
|
+
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class NotFoundError(WaveiumError):
|
|
53
|
+
"""Raised when a resource is not found (404)."""
|
|
54
|
+
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class ValidationError(WaveiumError):
|
|
59
|
+
"""Raised when request validation fails (400)."""
|
|
60
|
+
|
|
61
|
+
pass
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class RateLimitError(WaveiumError):
|
|
65
|
+
"""Raised when rate limit is exceeded (429)."""
|
|
66
|
+
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class APIError(WaveiumError):
|
|
71
|
+
"""Raised for general API errors (500, 502, 503, etc.)."""
|
|
72
|
+
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class NetworkError(WaveiumError):
|
|
77
|
+
"""Raised when network communication fails."""
|
|
78
|
+
|
|
79
|
+
pass
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class FileUploadError(WaveiumError):
|
|
83
|
+
"""Raised when file upload fails."""
|
|
84
|
+
|
|
85
|
+
pass
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class FileDownloadError(WaveiumError):
|
|
89
|
+
"""Raised when file download fails."""
|
|
90
|
+
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class ConfigurationError(WaveiumError):
|
|
95
|
+
"""Raised when SDK configuration is invalid."""
|
|
96
|
+
|
|
97
|
+
pass
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class StreamError(WaveiumError):
|
|
101
|
+
"""Base exception for SSE streaming errors."""
|
|
102
|
+
|
|
103
|
+
pass
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class StreamTimeoutError(StreamError):
|
|
107
|
+
"""Raised when total stream duration exceeds the configured limit."""
|
|
108
|
+
|
|
109
|
+
pass
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class StreamReconnectError(StreamError):
|
|
113
|
+
"""Raised when maximum SSE reconnection attempts are exceeded."""
|
|
114
|
+
|
|
115
|
+
pass
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class ExecutionFailedError(WaveiumError):
|
|
119
|
+
"""Raised by wait() when an execution reaches 'failed' status."""
|
|
120
|
+
|
|
121
|
+
def __init__(
|
|
122
|
+
self,
|
|
123
|
+
message: str,
|
|
124
|
+
execution_id: str,
|
|
125
|
+
error_message: Optional[str] = None,
|
|
126
|
+
code: Optional[str] = None,
|
|
127
|
+
details: Optional[Dict[str, Any]] = None,
|
|
128
|
+
) -> None:
|
|
129
|
+
super().__init__(message, code, details)
|
|
130
|
+
self.execution_id = execution_id
|
|
131
|
+
self.error_message = error_message
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"""Data models for the Waveium SDK."""
|
|
2
|
+
|
|
3
|
+
from .auth import (
|
|
4
|
+
AuthTokenResponse,
|
|
5
|
+
CreateTokenRequest,
|
|
6
|
+
TokenExchangeResponse,
|
|
7
|
+
TokenListResponse,
|
|
8
|
+
TokenUser,
|
|
9
|
+
)
|
|
10
|
+
from .common import (
|
|
11
|
+
BaseWaveiumModel,
|
|
12
|
+
ErrorDetail,
|
|
13
|
+
ErrorResponse,
|
|
14
|
+
MessageResponse,
|
|
15
|
+
PaginationInfo,
|
|
16
|
+
StorageStatsResponse,
|
|
17
|
+
)
|
|
18
|
+
from .environments import (
|
|
19
|
+
BoundsInput,
|
|
20
|
+
CreateEnvironmentRequest,
|
|
21
|
+
CreateEnvironmentResponse,
|
|
22
|
+
EnvironmentListItem,
|
|
23
|
+
EnvironmentResponse,
|
|
24
|
+
EnvironmentUploadCompleteResponse,
|
|
25
|
+
EnvironmentUploadRequest,
|
|
26
|
+
EnvironmentUploadResponse,
|
|
27
|
+
PaginatedEnvironmentResponse,
|
|
28
|
+
RouteInput,
|
|
29
|
+
SimilarEnvironmentResponse,
|
|
30
|
+
UpdateEnvironmentRequest,
|
|
31
|
+
)
|
|
32
|
+
from .executions import (
|
|
33
|
+
CreateExecutionRequest,
|
|
34
|
+
ExecutionListItem,
|
|
35
|
+
ExecutionProgressResponse,
|
|
36
|
+
ExecutionResponse,
|
|
37
|
+
PaginatedExecutionResponse,
|
|
38
|
+
UpdateExecutionRequest,
|
|
39
|
+
)
|
|
40
|
+
from .files import (
|
|
41
|
+
ArchiveDownloadResponse,
|
|
42
|
+
BatchDownloadFile,
|
|
43
|
+
BatchDownloadResponse,
|
|
44
|
+
EnvironmentFilesResponse,
|
|
45
|
+
ExecutionFilesResponse,
|
|
46
|
+
FileDownloadResponse,
|
|
47
|
+
FileInfo,
|
|
48
|
+
SceneFilesResponse,
|
|
49
|
+
)
|
|
50
|
+
from .organizations import (
|
|
51
|
+
CreateOrganizationRequest,
|
|
52
|
+
OrganizationResponse,
|
|
53
|
+
OrganizationStatsResponse,
|
|
54
|
+
UpdateOrganizationRequest,
|
|
55
|
+
)
|
|
56
|
+
from .projects import (
|
|
57
|
+
AddProjectEnvironmentRequest,
|
|
58
|
+
AddProjectMemberRequest,
|
|
59
|
+
CreateProjectRequest,
|
|
60
|
+
PaginatedProjectEnvironmentResponse,
|
|
61
|
+
PaginatedProjectMemberResponse,
|
|
62
|
+
PaginatedProjectResponse,
|
|
63
|
+
ProjectEnvironmentResponse,
|
|
64
|
+
ProjectListItem,
|
|
65
|
+
ProjectMemberResponse,
|
|
66
|
+
ProjectResponse,
|
|
67
|
+
UpdateProjectMemberRequest,
|
|
68
|
+
UpdateProjectRequest,
|
|
69
|
+
)
|
|
70
|
+
from .roles import (
|
|
71
|
+
PermissionResponse,
|
|
72
|
+
RoleDetailResponse,
|
|
73
|
+
RolePermissionResponse,
|
|
74
|
+
RoleResponse,
|
|
75
|
+
)
|
|
76
|
+
from .scenes import (
|
|
77
|
+
CreateSceneRequest,
|
|
78
|
+
PaginatedSceneResponse,
|
|
79
|
+
PatchSceneRequest,
|
|
80
|
+
SceneListItem,
|
|
81
|
+
SceneParameters,
|
|
82
|
+
SceneResponse,
|
|
83
|
+
SceneUploadCompleteResponse,
|
|
84
|
+
SceneUploadRequest,
|
|
85
|
+
SceneUploadResponse,
|
|
86
|
+
SimilarSceneResponse,
|
|
87
|
+
UpdateSceneRequest,
|
|
88
|
+
)
|
|
89
|
+
from .shares import (
|
|
90
|
+
CreateShareRequest,
|
|
91
|
+
PaginatedShareResponse,
|
|
92
|
+
ShareResponse,
|
|
93
|
+
)
|
|
94
|
+
from .teams import (
|
|
95
|
+
AddTeamMemberRequest,
|
|
96
|
+
CreateTeamRequest,
|
|
97
|
+
PaginatedTeamMemberResponse,
|
|
98
|
+
PaginatedTeamResponse,
|
|
99
|
+
TeamMemberResponse,
|
|
100
|
+
TeamResponse,
|
|
101
|
+
UpdateTeamRequest,
|
|
102
|
+
)
|
|
103
|
+
from .events import (
|
|
104
|
+
ErrorEvent,
|
|
105
|
+
ExecutionProgress,
|
|
106
|
+
ServerEvent,
|
|
107
|
+
StatusEvent,
|
|
108
|
+
TimeoutEvent,
|
|
109
|
+
)
|
|
110
|
+
from .users import (
|
|
111
|
+
AdminUpdateUserRequest,
|
|
112
|
+
CreateUserRequest,
|
|
113
|
+
PaginatedUserResponse,
|
|
114
|
+
RoleInUserResponse,
|
|
115
|
+
UpdateOwnUserRequest,
|
|
116
|
+
UserResponse,
|
|
117
|
+
UserRoleResponse,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
__all__ = [
|
|
121
|
+
# Common
|
|
122
|
+
"BaseWaveiumModel",
|
|
123
|
+
"ErrorDetail",
|
|
124
|
+
"ErrorResponse",
|
|
125
|
+
"MessageResponse",
|
|
126
|
+
"PaginationInfo",
|
|
127
|
+
"StorageStatsResponse",
|
|
128
|
+
# Auth
|
|
129
|
+
"AuthTokenResponse",
|
|
130
|
+
"CreateTokenRequest",
|
|
131
|
+
"TokenExchangeResponse",
|
|
132
|
+
"TokenListResponse",
|
|
133
|
+
"TokenUser",
|
|
134
|
+
# Environments
|
|
135
|
+
"BoundsInput",
|
|
136
|
+
"CreateEnvironmentRequest",
|
|
137
|
+
"CreateEnvironmentResponse",
|
|
138
|
+
"EnvironmentListItem",
|
|
139
|
+
"EnvironmentResponse",
|
|
140
|
+
"EnvironmentUploadCompleteResponse",
|
|
141
|
+
"EnvironmentUploadRequest",
|
|
142
|
+
"EnvironmentUploadResponse",
|
|
143
|
+
"PaginatedEnvironmentResponse",
|
|
144
|
+
"RouteInput",
|
|
145
|
+
"SimilarEnvironmentResponse",
|
|
146
|
+
"UpdateEnvironmentRequest",
|
|
147
|
+
# Scenes
|
|
148
|
+
"CreateSceneRequest",
|
|
149
|
+
"PaginatedSceneResponse",
|
|
150
|
+
"PatchSceneRequest",
|
|
151
|
+
"SceneListItem",
|
|
152
|
+
"SceneParameters",
|
|
153
|
+
"SceneResponse",
|
|
154
|
+
"SceneUploadCompleteResponse",
|
|
155
|
+
"SceneUploadRequest",
|
|
156
|
+
"SceneUploadResponse",
|
|
157
|
+
"SimilarSceneResponse",
|
|
158
|
+
"UpdateSceneRequest",
|
|
159
|
+
# Executions
|
|
160
|
+
"CreateExecutionRequest",
|
|
161
|
+
"ExecutionListItem",
|
|
162
|
+
"ExecutionProgressResponse",
|
|
163
|
+
"ExecutionResponse",
|
|
164
|
+
"PaginatedExecutionResponse",
|
|
165
|
+
"UpdateExecutionRequest",
|
|
166
|
+
# Files
|
|
167
|
+
"ArchiveDownloadResponse",
|
|
168
|
+
"BatchDownloadFile",
|
|
169
|
+
"BatchDownloadResponse",
|
|
170
|
+
"EnvironmentFilesResponse",
|
|
171
|
+
"ExecutionFilesResponse",
|
|
172
|
+
"FileDownloadResponse",
|
|
173
|
+
"FileInfo",
|
|
174
|
+
"SceneFilesResponse",
|
|
175
|
+
# Organizations
|
|
176
|
+
"CreateOrganizationRequest",
|
|
177
|
+
"OrganizationResponse",
|
|
178
|
+
"OrganizationStatsResponse",
|
|
179
|
+
"UpdateOrganizationRequest",
|
|
180
|
+
# Projects
|
|
181
|
+
"AddProjectEnvironmentRequest",
|
|
182
|
+
"AddProjectMemberRequest",
|
|
183
|
+
"CreateProjectRequest",
|
|
184
|
+
"PaginatedProjectEnvironmentResponse",
|
|
185
|
+
"PaginatedProjectMemberResponse",
|
|
186
|
+
"PaginatedProjectResponse",
|
|
187
|
+
"ProjectEnvironmentResponse",
|
|
188
|
+
"ProjectListItem",
|
|
189
|
+
"ProjectMemberResponse",
|
|
190
|
+
"ProjectResponse",
|
|
191
|
+
"UpdateProjectMemberRequest",
|
|
192
|
+
"UpdateProjectRequest",
|
|
193
|
+
# Roles
|
|
194
|
+
"PermissionResponse",
|
|
195
|
+
"RoleDetailResponse",
|
|
196
|
+
"RolePermissionResponse",
|
|
197
|
+
"RoleResponse",
|
|
198
|
+
# Shares
|
|
199
|
+
"CreateShareRequest",
|
|
200
|
+
"PaginatedShareResponse",
|
|
201
|
+
"ShareResponse",
|
|
202
|
+
# Teams
|
|
203
|
+
"AddTeamMemberRequest",
|
|
204
|
+
"CreateTeamRequest",
|
|
205
|
+
"PaginatedTeamMemberResponse",
|
|
206
|
+
"PaginatedTeamResponse",
|
|
207
|
+
"TeamMemberResponse",
|
|
208
|
+
"TeamResponse",
|
|
209
|
+
"UpdateTeamRequest",
|
|
210
|
+
# Events (SSE)
|
|
211
|
+
"ErrorEvent",
|
|
212
|
+
"ExecutionProgress",
|
|
213
|
+
"ServerEvent",
|
|
214
|
+
"StatusEvent",
|
|
215
|
+
"TimeoutEvent",
|
|
216
|
+
# Users
|
|
217
|
+
"AdminUpdateUserRequest",
|
|
218
|
+
"CreateUserRequest",
|
|
219
|
+
"PaginatedUserResponse",
|
|
220
|
+
"RoleInUserResponse",
|
|
221
|
+
"UpdateOwnUserRequest",
|
|
222
|
+
"UserResponse",
|
|
223
|
+
"UserRoleResponse",
|
|
224
|
+
]
|
waveium/models/auth.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Authentication models for the Waveium SDK."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import List, Optional
|
|
5
|
+
|
|
6
|
+
from pydantic import Field
|
|
7
|
+
|
|
8
|
+
from .common import BaseWaveiumModel
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CreateTokenRequest(BaseWaveiumModel):
|
|
12
|
+
"""Request to create a new API token."""
|
|
13
|
+
|
|
14
|
+
name: str = Field(description="Name/description for the API key")
|
|
15
|
+
expires_days: int = Field(
|
|
16
|
+
default=30,
|
|
17
|
+
ge=1,
|
|
18
|
+
le=365,
|
|
19
|
+
description="Number of days until token expires (1-365)",
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TokenUser(BaseWaveiumModel):
|
|
24
|
+
"""User information included in token response."""
|
|
25
|
+
|
|
26
|
+
id: str = Field(description="User ID")
|
|
27
|
+
email: str = Field(description="User email")
|
|
28
|
+
name: Optional[str] = Field(default=None, description="User name")
|
|
29
|
+
organization_id: str = Field(description="Organization ID")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class TokenExchangeResponse(BaseWaveiumModel):
|
|
33
|
+
"""Response from token exchange endpoint."""
|
|
34
|
+
|
|
35
|
+
api_key: str = Field(description="Generated API key (only returned once)")
|
|
36
|
+
expires_in: int = Field(description="Expiration time in seconds")
|
|
37
|
+
user: TokenUser = Field(description="User information")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class AuthTokenResponse(BaseWaveiumModel):
|
|
41
|
+
"""Individual API token information."""
|
|
42
|
+
|
|
43
|
+
id: str = Field(description="Token ID")
|
|
44
|
+
name: str = Field(description="Token name/description")
|
|
45
|
+
is_active: bool = Field(description="Whether the token is active")
|
|
46
|
+
expires_at: Optional[datetime] = Field(
|
|
47
|
+
default=None, description="When the token expires"
|
|
48
|
+
)
|
|
49
|
+
created_at: datetime = Field(description="When the token was created")
|
|
50
|
+
last_used: Optional[datetime] = Field(
|
|
51
|
+
default=None, description="When the token was last used"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class TokenListResponse(BaseWaveiumModel):
|
|
56
|
+
"""Response containing list of tokens."""
|
|
57
|
+
|
|
58
|
+
tokens: List[AuthTokenResponse] = Field(description="List of tokens")
|
waveium/models/common.py
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""Common models used across the Waveium SDK."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import Any, Dict, List, Literal, Optional, Sequence # noqa: F401
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ErrorDetail(BaseModel):
|
|
10
|
+
"""Error detail from API responses."""
|
|
11
|
+
|
|
12
|
+
code: str = Field(description="Error code")
|
|
13
|
+
message: str = Field(description="Error message")
|
|
14
|
+
details: Optional[Dict[str, Any]] = Field(
|
|
15
|
+
default=None, description="Additional error details"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ErrorResponse(BaseModel):
|
|
20
|
+
"""Error response from the API."""
|
|
21
|
+
|
|
22
|
+
error: ErrorDetail = Field(description="Error information")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class MessageResponse(BaseModel):
|
|
26
|
+
"""Generic message response."""
|
|
27
|
+
|
|
28
|
+
message: str = Field(description="Response message")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class PaginationInfo(BaseModel):
|
|
32
|
+
"""Pagination information for list responses."""
|
|
33
|
+
|
|
34
|
+
page: int = Field(description="Current page number (1-based)")
|
|
35
|
+
page_size: int = Field(description="Items per page")
|
|
36
|
+
total: int = Field(description="Total number of items")
|
|
37
|
+
total_pages: int = Field(description="Total number of pages")
|
|
38
|
+
has_next: bool = Field(description="Whether there is a next page")
|
|
39
|
+
has_prev: bool = Field(description="Whether there is a previous page")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class BaseWaveiumModel(BaseModel):
|
|
43
|
+
"""Base model for all Waveium API models."""
|
|
44
|
+
|
|
45
|
+
model_config = {"populate_by_name": True, "use_enum_values": True}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# Status type aliases
|
|
49
|
+
EnvironmentStatus = Literal[
|
|
50
|
+
"pending",
|
|
51
|
+
"queued",
|
|
52
|
+
"running",
|
|
53
|
+
"completed",
|
|
54
|
+
"failed",
|
|
55
|
+
"cancelled",
|
|
56
|
+
]
|
|
57
|
+
SceneStatus = Literal[
|
|
58
|
+
"pending",
|
|
59
|
+
"pending_environment",
|
|
60
|
+
"queued",
|
|
61
|
+
"running",
|
|
62
|
+
"completed",
|
|
63
|
+
"failed",
|
|
64
|
+
"cancelled",
|
|
65
|
+
]
|
|
66
|
+
ExecutionStatus = Literal[
|
|
67
|
+
"queued",
|
|
68
|
+
"running",
|
|
69
|
+
"completed",
|
|
70
|
+
"failed",
|
|
71
|
+
"cancelled",
|
|
72
|
+
]
|
|
73
|
+
EnvironmentType = Literal["outdoor", "indoor", "underground"]
|
|
74
|
+
ScopeFilter = Literal["mine", "shared", "org"]
|
|
75
|
+
SceneScopeFilter = Literal["mine", "shared", "project", "org"]
|
|
76
|
+
PermissionLevel = Literal["view", "use", "full"]
|
|
77
|
+
ShareTargetType = Literal["user", "team", "organization"]
|
|
78
|
+
ProjectRole = Literal["owner", "admin", "member"]
|
|
79
|
+
TeamRole = Literal["member", "admin"]
|
|
80
|
+
TransportMode = Literal["walk", "bike", "drive"]
|
|
81
|
+
SubscriptionTier = Literal["free", "starter", "pro", "enterprise"]
|
|
82
|
+
ProjectMemberType = Literal["user", "team"]
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class StorageStatsResponse(BaseModel):
|
|
86
|
+
"""Storage statistics for a resource."""
|
|
87
|
+
|
|
88
|
+
total_files: int = Field(description="Total number of files")
|
|
89
|
+
total_size_bytes: int = Field(description="Total size in bytes")
|
|
90
|
+
total_size_mb: float = Field(description="Total size in megabytes")
|
|
91
|
+
objects: Optional[Dict[str, Any]] = Field(
|
|
92
|
+
default=None, description="Object file statistics"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _fmt_dt(dt: Optional[datetime]) -> str:
|
|
97
|
+
"""Format a datetime for table display."""
|
|
98
|
+
if dt is None:
|
|
99
|
+
return "-"
|
|
100
|
+
return dt.strftime("%Y-%m-%d %H:%M")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _tabulate(
|
|
104
|
+
headers: Sequence[str],
|
|
105
|
+
rows: Sequence[Sequence[str]],
|
|
106
|
+
) -> str:
|
|
107
|
+
"""Render a simple aligned text table.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
headers: Column header strings.
|
|
111
|
+
rows: List of row tuples (same length as headers).
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
Formatted table string with aligned columns.
|
|
115
|
+
"""
|
|
116
|
+
# Compute column widths from headers and data
|
|
117
|
+
widths = [len(h) for h in headers]
|
|
118
|
+
for row in rows:
|
|
119
|
+
for i, cell in enumerate(row):
|
|
120
|
+
widths[i] = max(widths[i], len(cell))
|
|
121
|
+
|
|
122
|
+
# Build format string
|
|
123
|
+
fmt = " ".join(f"{{:<{w}}}" for w in widths)
|
|
124
|
+
|
|
125
|
+
lines = [fmt.format(*headers)]
|
|
126
|
+
lines.append(" ".join("-" * w for w in widths))
|
|
127
|
+
for row in rows:
|
|
128
|
+
lines.append(fmt.format(*row))
|
|
129
|
+
|
|
130
|
+
return "\n".join(lines)
|