robot-wrapper-sdk 0.2.8__tar.gz → 0.2.10__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.
Files changed (19) hide show
  1. {robot_wrapper_sdk-0.2.8 → robot_wrapper_sdk-0.2.10}/PKG-INFO +1 -1
  2. {robot_wrapper_sdk-0.2.8 → robot_wrapper_sdk-0.2.10}/pyproject.toml +1 -1
  3. {robot_wrapper_sdk-0.2.8 → robot_wrapper_sdk-0.2.10}/robot_sdk/domain/entities.py +12 -6
  4. {robot_wrapper_sdk-0.2.8 → robot_wrapper_sdk-0.2.10}/robot_sdk/infrastructure/api_client.py +58 -10
  5. {robot_wrapper_sdk-0.2.8 → robot_wrapper_sdk-0.2.10}/robot_sdk/infrastructure/robot_api_repository.py +23 -8
  6. {robot_wrapper_sdk-0.2.8 → robot_wrapper_sdk-0.2.10}/robot_wrapper_sdk.egg-info/PKG-INFO +1 -1
  7. {robot_wrapper_sdk-0.2.8 → robot_wrapper_sdk-0.2.10}/README.md +0 -0
  8. {robot_wrapper_sdk-0.2.8 → robot_wrapper_sdk-0.2.10}/examples/main.py +0 -0
  9. {robot_wrapper_sdk-0.2.8 → robot_wrapper_sdk-0.2.10}/robot_sdk/__init__.py +0 -0
  10. {robot_wrapper_sdk-0.2.8 → robot_wrapper_sdk-0.2.10}/robot_sdk/application/__init__.py +0 -0
  11. {robot_wrapper_sdk-0.2.8 → robot_wrapper_sdk-0.2.10}/robot_sdk/application/usecases.py +0 -0
  12. {robot_wrapper_sdk-0.2.8 → robot_wrapper_sdk-0.2.10}/robot_sdk/domain/__init__.py +0 -0
  13. {robot_wrapper_sdk-0.2.8 → robot_wrapper_sdk-0.2.10}/robot_sdk/domain/repositories.py +0 -0
  14. {robot_wrapper_sdk-0.2.8 → robot_wrapper_sdk-0.2.10}/robot_sdk/infrastructure/__init__.py +0 -0
  15. {robot_wrapper_sdk-0.2.8 → robot_wrapper_sdk-0.2.10}/robot_wrapper_sdk.egg-info/SOURCES.txt +0 -0
  16. {robot_wrapper_sdk-0.2.8 → robot_wrapper_sdk-0.2.10}/robot_wrapper_sdk.egg-info/dependency_links.txt +0 -0
  17. {robot_wrapper_sdk-0.2.8 → robot_wrapper_sdk-0.2.10}/robot_wrapper_sdk.egg-info/requires.txt +0 -0
  18. {robot_wrapper_sdk-0.2.8 → robot_wrapper_sdk-0.2.10}/robot_wrapper_sdk.egg-info/top_level.txt +0 -0
  19. {robot_wrapper_sdk-0.2.8 → robot_wrapper_sdk-0.2.10}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: robot-wrapper-sdk
3
- Version: 0.2.8
3
+ Version: 0.2.10
4
4
  Summary: Robot Platform API SDK
5
5
  Author-email: GH Robot Platform Team <team@ghrobot.com>
6
6
  License: MIT
@@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta"
8
8
 
9
9
  [project]
10
10
  name = "robot-wrapper-sdk"
11
- version = "0.2.8"
11
+ version = "0.2.10"
12
12
  description = "Robot Platform API SDK"
13
13
  readme = "README.md"
14
14
  requires-python = ">=3.10"
@@ -32,25 +32,23 @@ def normalize_robot_auth_status(status: str | RobotAuthStatus) -> str:
32
32
  def ensure_valid_robot_status(status: str) -> None:
33
33
  allowed = {s.value for s in RobotStatus}
34
34
  if status not in allowed:
35
- raise ValueError(f"invalid status: {status}. allowed={sorted(allowed)}")
35
+ raise ValueError(
36
+ f"invalid status: {status}. allowed={sorted(allowed)}")
36
37
 
37
38
 
38
39
  def ensure_valid_robot_auth_status(status: str) -> None:
39
40
  allowed = {s.value for s in RobotAuthStatus}
40
41
  if status not in allowed:
41
- raise ValueError(f"invalid auth_status: {status}. allowed={sorted(allowed)}")
42
+ raise ValueError(
43
+ f"invalid auth_status: {status}. allowed={sorted(allowed)}")
42
44
 
43
45
 
44
46
  def ensure_valid_worker_robot_auth_status(status: str) -> None:
45
47
  ensure_valid_robot_auth_status(status)
46
- if status == RobotAuthStatus.LOGIN_ERROR.value:
47
- raise ValueError("auth_status login_error is not allowed for this endpoint")
48
48
 
49
49
 
50
50
  def ensure_valid_worker_robot_status(status: str) -> None:
51
51
  ensure_valid_robot_status(status)
52
- if status == RobotStatus.CHECKPOINT.value:
53
- raise ValueError("status checkpoint is not allowed for this endpoint")
54
52
 
55
53
 
56
54
  def ensure_valid_metadata(metadata: Dict[str, Any]) -> None:
@@ -74,16 +72,19 @@ class Robot:
74
72
  username: str
75
73
  platform: str
76
74
  status: str
75
+ auth_status: str
77
76
  metadata: Dict[str, Any] = field(default_factory=dict)
78
77
  project_id: Optional[str] = None
79
78
  primary_environment: Optional[str] = None
80
79
  created_at: Optional[str] = None
81
80
 
81
+
82
82
  @dataclass
83
83
  class RobotSecrets:
84
84
  password: str
85
85
  two_fa_secret: str
86
86
 
87
+
87
88
  @dataclass
88
89
  class RobotActivity:
89
90
  id: str
@@ -91,25 +92,30 @@ class RobotActivity:
91
92
  status: str
92
93
  message: str
93
94
 
95
+
94
96
  @dataclass
95
97
  class AcquireNeedLoginRequest:
96
98
  limit: int = 20
97
99
  lock_minutes: int = 30
98
100
 
101
+
99
102
  @dataclass
100
103
  class AcquireUnhardenedRequest:
101
104
  limit: int = 20
102
105
  lock_minutes: int = 30
103
106
  min_age_days: int = 0
104
107
 
108
+
105
109
  @dataclass
106
110
  class UpdateAuthStatusRequest:
107
111
  auth_status: str
108
112
 
113
+
109
114
  @dataclass
110
115
  class UpdateSecurityHardenedRequest:
111
116
  security_hardened: bool
112
117
 
118
+
113
119
  @dataclass
114
120
  class StandardResponse:
115
121
  status: str
@@ -3,6 +3,54 @@ import os
3
3
  from typing import Dict, Any, Optional
4
4
  from httpx import RemoteProtocolError
5
5
 
6
+
7
+ def _build_httpx_client(base_url: str, auth: httpx.Auth, proxy_url: Optional[str]) -> httpx.Client:
8
+ kwargs: Dict[str, Any] = {"base_url": base_url, "auth": auth}
9
+ if proxy_url:
10
+ kwargs["proxy"] = proxy_url
11
+ try:
12
+ return httpx.Client(**kwargs)
13
+ except TypeError:
14
+ if "proxy" in kwargs:
15
+ kwargs["proxies"] = kwargs.pop("proxy")
16
+ return httpx.Client(**kwargs)
17
+
18
+
19
+ def _build_async_httpx_client(base_url: str, auth: httpx.Auth, proxy_url: Optional[str]) -> httpx.AsyncClient:
20
+ kwargs: Dict[str, Any] = {"base_url": base_url, "auth": auth}
21
+ if proxy_url:
22
+ kwargs["proxy"] = proxy_url
23
+ try:
24
+ return httpx.AsyncClient(**kwargs)
25
+ except TypeError:
26
+ if "proxy" in kwargs:
27
+ kwargs["proxies"] = kwargs.pop("proxy")
28
+ return httpx.AsyncClient(**kwargs)
29
+
30
+
31
+ def _build_plain_httpx_client(proxy_url: Optional[str]) -> httpx.Client:
32
+ kwargs: Dict[str, Any] = {}
33
+ if proxy_url:
34
+ kwargs["proxy"] = proxy_url
35
+ try:
36
+ return httpx.Client(**kwargs)
37
+ except TypeError:
38
+ if "proxy" in kwargs:
39
+ kwargs["proxies"] = kwargs.pop("proxy")
40
+ return httpx.Client(**kwargs)
41
+
42
+
43
+ def _build_plain_async_httpx_client(proxy_url: Optional[str]) -> httpx.AsyncClient:
44
+ kwargs: Dict[str, Any] = {}
45
+ if proxy_url:
46
+ kwargs["proxy"] = proxy_url
47
+ try:
48
+ return httpx.AsyncClient(**kwargs)
49
+ except TypeError:
50
+ if "proxy" in kwargs:
51
+ kwargs["proxies"] = kwargs.pop("proxy")
52
+ return httpx.AsyncClient(**kwargs)
53
+
6
54
  def _extract_tokens(payload: Dict[str, Any]) -> tuple[Optional[str], Optional[str]]:
7
55
  data = payload.get("data") if isinstance(payload, dict) else None
8
56
  if isinstance(data, dict):
@@ -69,10 +117,10 @@ class HTTPClient:
69
117
  self.access_token: Optional[str] = None
70
118
  self.refresh_token: Optional[str] = None
71
119
 
72
- self.client = httpx.Client(
120
+ self.client = _build_httpx_client(
73
121
  base_url=self.base_url,
74
122
  auth=RobotAuth(self._get_or_login, self._refresh),
75
- proxy=self.proxy_url
123
+ proxy_url=self.proxy_url,
76
124
  )
77
125
 
78
126
  def _get_or_login(self) -> str:
@@ -84,7 +132,7 @@ class HTTPClient:
84
132
  return token
85
133
 
86
134
  def _login(self) -> None:
87
- with httpx.Client(proxy=self.proxy_url) as c:
135
+ with _build_plain_httpx_client(self.proxy_url) as c:
88
136
  resp = c.post(
89
137
  f"{self.base_url}/api/v1/developer/auth/token",
90
138
  json={"app_id": self.app_id, "app_secret": self.app_secret}
@@ -107,7 +155,7 @@ class HTTPClient:
107
155
  raise RuntimeError("Empty access token after login")
108
156
  return token
109
157
 
110
- with httpx.Client(proxy=self.proxy_url) as c:
158
+ with _build_plain_httpx_client(self.proxy_url) as c:
111
159
  resp = c.post(
112
160
  f"{self.base_url}/api/v1/developer/auth/refresh",
113
161
  json={"refresh_token": self.refresh_token}
@@ -146,10 +194,10 @@ class HTTPClient:
146
194
  raise
147
195
  last_err = err
148
196
  self.client.close()
149
- self.client = httpx.Client(
197
+ self.client = _build_httpx_client(
150
198
  base_url=self.base_url,
151
199
  auth=RobotAuth(self._get_or_login, self._refresh),
152
- proxy=self.proxy_url,
200
+ proxy_url=self.proxy_url,
153
201
  )
154
202
 
155
203
  if resp is None:
@@ -183,10 +231,10 @@ class AsyncHTTPClient:
183
231
  self.access_token: Optional[str] = None
184
232
  self.refresh_token: Optional[str] = None
185
233
 
186
- self.client = httpx.AsyncClient(
234
+ self.client = _build_async_httpx_client(
187
235
  base_url=self.base_url,
188
236
  auth=AsyncRobotAuth(self._get_or_login, self._refresh),
189
- proxy=self.proxy_url
237
+ proxy_url=self.proxy_url,
190
238
  )
191
239
 
192
240
  async def _get_or_login(self) -> str:
@@ -198,7 +246,7 @@ class AsyncHTTPClient:
198
246
  return token
199
247
 
200
248
  async def _login(self) -> None:
201
- async with httpx.AsyncClient(proxy=self.proxy_url) as c:
249
+ async with _build_plain_async_httpx_client(self.proxy_url) as c:
202
250
  resp = await c.post(
203
251
  f"{self.base_url}/api/v1/developer/auth/token",
204
252
  json={"app_id": self.app_id, "app_secret": self.app_secret}
@@ -220,7 +268,7 @@ class AsyncHTTPClient:
220
268
  raise RuntimeError("Empty access token after login")
221
269
  return token
222
270
 
223
- async with httpx.AsyncClient(proxy=self.proxy_url) as c:
271
+ async with _build_plain_async_httpx_client(self.proxy_url) as c:
224
272
  resp = await c.post(
225
273
  f"{self.base_url}/api/v1/developer/auth/refresh",
226
274
  json={"refresh_token": self.refresh_token}
@@ -39,13 +39,26 @@ def _extract_items(resp: Dict[str, Any]) -> List[Dict[str, Any]]:
39
39
  return [item for item in nested if isinstance(item, dict)]
40
40
  return []
41
41
 
42
+
43
+ def _to_robot_payload(item: Dict[str, Any]) -> Dict[str, Any]:
44
+ payload = dict(item)
45
+ payload.setdefault("auth_status", "")
46
+ payload.setdefault("metadata", {})
47
+ return payload
48
+
49
+
50
+ def _to_robots(items: List[Dict[str, Any]]) -> List[Robot]:
51
+ return [Robot(**_to_robot_payload(item)) for item in items]
52
+
42
53
  class RobotAPIRepository(RobotRepository):
43
54
  def __init__(self, client: HTTPClient):
44
55
  self.client = client
45
56
 
46
57
  def find_by_id(self, robot_id: str) -> Optional[Robot]:
47
58
  resp = self.client.request("GET", f"/robots/{robot_id}")
48
- return Robot(**resp) if resp else None
59
+ payload = resp.get("data") if isinstance(resp, dict) else None
60
+ data = payload if isinstance(payload, dict) else resp
61
+ return Robot(**data) if isinstance(data, dict) else None
49
62
 
50
63
  def update_status(self, robot_id: str, status: str | RobotStatus) -> None:
51
64
  normalized_status = normalize_robot_status(status)
@@ -76,7 +89,7 @@ class RobotAPIRepository(RobotRepository):
76
89
  json={"limit": limit, "lock_minutes": lock_minutes},
77
90
  )
78
91
  data = _extract_items(resp)
79
- return [Robot(**r) for r in data]
92
+ return _to_robots(data)
80
93
 
81
94
  def acquire_unhardened(self, limit: int = 20, lock_minutes: int = 30, min_age_days: int = 0) -> List[Robot]:
82
95
  resp = self.client.request(
@@ -85,7 +98,7 @@ class RobotAPIRepository(RobotRepository):
85
98
  json={"limit": limit, "lock_minutes": lock_minutes, "min_age_days": min_age_days},
86
99
  )
87
100
  data = _extract_items(resp)
88
- return [Robot(**r) for r in data]
101
+ return _to_robots(data)
89
102
 
90
103
  def update_auth_status(self, robot_id: str, auth_status: str | RobotAuthStatus) -> None:
91
104
  normalized_auth_status = normalize_robot_auth_status(auth_status)
@@ -106,7 +119,7 @@ class RobotAPIRepository(RobotRepository):
106
119
  def update_metadata(self, robot_id: str, metadata: Dict[str, Any]) -> None:
107
120
  self.client.request(
108
121
  "PATCH",
109
- f"/robots/{robot_id}",
122
+ f"/robots/{robot_id}/metadata",
110
123
  json={"metadata": metadata},
111
124
  )
112
125
 
@@ -116,7 +129,9 @@ class AsyncRobotAPIRepository(AsyncRobotRepository):
116
129
 
117
130
  async def find_by_id(self, robot_id: str) -> Optional[Robot]:
118
131
  resp = await self.client.request("GET", f"/robots/{robot_id}")
119
- return Robot(**resp) if resp else None
132
+ payload = resp.get("data") if isinstance(resp, dict) else None
133
+ data = payload if isinstance(payload, dict) else resp
134
+ return Robot(**data) if isinstance(data, dict) else None
120
135
 
121
136
  async def update_status(self, robot_id: str, status: str | RobotStatus) -> None:
122
137
  normalized_status = normalize_robot_status(status)
@@ -147,7 +162,7 @@ class AsyncRobotAPIRepository(AsyncRobotRepository):
147
162
  json={"limit": limit, "lock_minutes": lock_minutes},
148
163
  )
149
164
  data = _extract_items(resp)
150
- return [Robot(**r) for r in data]
165
+ return _to_robots(data)
151
166
 
152
167
  async def acquire_unhardened(self, limit: int = 20, lock_minutes: int = 30, min_age_days: int = 0) -> List[Robot]:
153
168
  resp = await self.client.request(
@@ -156,7 +171,7 @@ class AsyncRobotAPIRepository(AsyncRobotRepository):
156
171
  json={"limit": limit, "lock_minutes": lock_minutes, "min_age_days": min_age_days},
157
172
  )
158
173
  data = _extract_items(resp)
159
- return [Robot(**r) for r in data]
174
+ return _to_robots(data)
160
175
 
161
176
  async def update_auth_status(self, robot_id: str, auth_status: str | RobotAuthStatus) -> None:
162
177
  normalized_auth_status = normalize_robot_auth_status(auth_status)
@@ -177,6 +192,6 @@ class AsyncRobotAPIRepository(AsyncRobotRepository):
177
192
  async def update_metadata(self, robot_id: str, metadata: Dict[str, Any]) -> None:
178
193
  await self.client.request(
179
194
  "PATCH",
180
- f"/robots/{robot_id}",
195
+ f"/robots/{robot_id}/metadata",
181
196
  json={"metadata": metadata},
182
197
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: robot-wrapper-sdk
3
- Version: 0.2.8
3
+ Version: 0.2.10
4
4
  Summary: Robot Platform API SDK
5
5
  Author-email: GH Robot Platform Team <team@ghrobot.com>
6
6
  License: MIT