orgo 0.0.39__py3-none-any.whl → 0.0.40__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.
- orgo/__init__.py +11 -8
- orgo/api/__init__.py +5 -5
- orgo/api/client.py +227 -227
- orgo/computer.py +471 -466
- orgo/forge.py +176 -176
- orgo/project.py +86 -86
- orgo/prompt.py +1015 -1015
- orgo/utils/__init__.py +5 -5
- orgo/utils/auth.py +16 -16
- {orgo-0.0.39.dist-info → orgo-0.0.40.dist-info}/METADATA +47 -47
- orgo-0.0.40.dist-info/RECORD +13 -0
- {orgo-0.0.39.dist-info → orgo-0.0.40.dist-info}/WHEEL +1 -1
- orgo-0.0.39.dist-info/RECORD +0 -13
- {orgo-0.0.39.dist-info → orgo-0.0.40.dist-info}/top_level.txt +0 -0
orgo/__init__.py
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
# src/orgo/__init__.py
|
|
2
|
-
"""Orgo SDK: Desktop infrastructure for AI agents"""
|
|
3
|
-
|
|
4
|
-
from .project import Project
|
|
5
|
-
from .computer import Computer
|
|
6
|
-
from .forge import Forge
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
# src/orgo/__init__.py
|
|
2
|
+
"""Orgo SDK: Desktop infrastructure for AI agents"""
|
|
3
|
+
|
|
4
|
+
from .project import Project
|
|
5
|
+
from .computer import Computer
|
|
6
|
+
from .forge import Forge
|
|
7
|
+
|
|
8
|
+
# Workspace is an alias for Project (preferred name going forward)
|
|
9
|
+
Workspace = Project
|
|
10
|
+
|
|
11
|
+
__all__ = ["Project", "Workspace", "Computer", "Forge"]
|
orgo/api/__init__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# src/orgo/api/__init__.py
|
|
2
|
-
"""API package for Orgo SDK"""
|
|
3
|
-
|
|
4
|
-
from .client import ApiClient
|
|
5
|
-
|
|
1
|
+
# src/orgo/api/__init__.py
|
|
2
|
+
"""API package for Orgo SDK"""
|
|
3
|
+
|
|
4
|
+
from .client import ApiClient
|
|
5
|
+
|
|
6
6
|
__all__ = ["ApiClient"]
|
orgo/api/client.py
CHANGED
|
@@ -1,227 +1,227 @@
|
|
|
1
|
-
"""API client for Orgo service"""
|
|
2
|
-
|
|
3
|
-
import requests
|
|
4
|
-
from typing import Dict, Any, Optional, List
|
|
5
|
-
import logging
|
|
6
|
-
|
|
7
|
-
from orgo.utils.auth import get_api_key
|
|
8
|
-
|
|
9
|
-
logger = logging.getLogger(__name__)
|
|
10
|
-
|
|
11
|
-
class ApiClient:
|
|
12
|
-
def __init__(self, api_key: Optional[str] = None, base_url: Optional[str] = None):
|
|
13
|
-
self.api_key = get_api_key(api_key)
|
|
14
|
-
self.base_url = base_url or "https://www.orgo.ai/api"
|
|
15
|
-
self.session = requests.Session()
|
|
16
|
-
self.session.headers.update({
|
|
17
|
-
"Authorization": f"Bearer {self.api_key}",
|
|
18
|
-
"Content-Type": "application/json",
|
|
19
|
-
"Accept": "application/json"
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
def _request(self, method: str, endpoint: str, data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
23
|
-
url = f"{self.base_url}/{endpoint}"
|
|
24
|
-
|
|
25
|
-
try:
|
|
26
|
-
if method.upper() == "GET":
|
|
27
|
-
response = self.session.get(url, params=data)
|
|
28
|
-
else:
|
|
29
|
-
response = self.session.request(method, url, json=data)
|
|
30
|
-
|
|
31
|
-
# Handle 405 specifically for better debugging
|
|
32
|
-
if response.status_code == 405:
|
|
33
|
-
logger.error(f"Method Not Allowed: {method} {url}")
|
|
34
|
-
logger.error(f"Response: {response.text}")
|
|
35
|
-
|
|
36
|
-
response.raise_for_status()
|
|
37
|
-
return response.json()
|
|
38
|
-
except requests.exceptions.RequestException as e:
|
|
39
|
-
# Log the full error for debugging
|
|
40
|
-
logger.debug(f"API request failed: {method} {url}", exc_info=True)
|
|
41
|
-
|
|
42
|
-
if hasattr(e, 'response') and e.response is not None:
|
|
43
|
-
try:
|
|
44
|
-
error_data = e.response.json()
|
|
45
|
-
if 'error' in error_data:
|
|
46
|
-
raise Exception(error_data['error']) from None
|
|
47
|
-
except (ValueError, KeyError):
|
|
48
|
-
pass
|
|
49
|
-
raise Exception(f"Request failed with status {e.response.status_code}") from None
|
|
50
|
-
|
|
51
|
-
# Generic error message without exposing internal details
|
|
52
|
-
raise Exception("Failed to connect to Orgo service. Please check your connection and try again.") from None
|
|
53
|
-
|
|
54
|
-
# Project methods
|
|
55
|
-
def create_project(self, name: str) -> Dict[str, Any]:
|
|
56
|
-
"""Create a new named project"""
|
|
57
|
-
return self._request("POST", "projects", {"name": name})
|
|
58
|
-
|
|
59
|
-
def get_project_by_name(self, name: str) -> Dict[str, Any]:
|
|
60
|
-
"""Get project details by name"""
|
|
61
|
-
projects = self.list_projects()
|
|
62
|
-
for project in projects:
|
|
63
|
-
if project.get("name") == name:
|
|
64
|
-
return project
|
|
65
|
-
raise Exception(f"Project '{name}' not found") from None
|
|
66
|
-
|
|
67
|
-
def get_project(self, project_id: str) -> Dict[str, Any]:
|
|
68
|
-
"""Get project details by ID"""
|
|
69
|
-
return self._request("GET", f"projects/{project_id}")
|
|
70
|
-
|
|
71
|
-
def list_projects(self) -> List[Dict[str, Any]]:
|
|
72
|
-
"""List all projects"""
|
|
73
|
-
response = self._request("GET", "projects")
|
|
74
|
-
return response.get("projects", [])
|
|
75
|
-
|
|
76
|
-
def delete_project(self, project_id: str) -> Dict[str, Any]:
|
|
77
|
-
"""Delete a project and all its computers"""
|
|
78
|
-
return self._request("DELETE", f"projects/{project_id}")
|
|
79
|
-
|
|
80
|
-
# Computer methods
|
|
81
|
-
def create_computer(self, project_id: str, computer_name: str,
|
|
82
|
-
os: str = "linux", ram: int = 2, cpu: int = 2,
|
|
83
|
-
gpu: str = "none", image: Optional[str] = None) -> Dict[str, Any]:
|
|
84
|
-
"""Create a new computer within a project"""
|
|
85
|
-
data = {
|
|
86
|
-
"
|
|
87
|
-
"name": computer_name,
|
|
88
|
-
"os": os,
|
|
89
|
-
"ram": ram,
|
|
90
|
-
"cpu": cpu,
|
|
91
|
-
"gpu": gpu
|
|
92
|
-
}
|
|
93
|
-
if image:
|
|
94
|
-
data["image"] = image
|
|
95
|
-
|
|
96
|
-
return self._request("POST", "computers", data)
|
|
97
|
-
|
|
98
|
-
def list_computers(self, project_id: str) -> List[Dict[str, Any]]:
|
|
99
|
-
"""List all computers in a project"""
|
|
100
|
-
project = self.get_project(project_id)
|
|
101
|
-
return project.get("desktops", [])
|
|
102
|
-
|
|
103
|
-
def get_computer(self, computer_id: str) -> Dict[str, Any]:
|
|
104
|
-
"""Get computer details"""
|
|
105
|
-
return self._request("GET", f"computers/{computer_id}")
|
|
106
|
-
|
|
107
|
-
def delete_computer(self, computer_id: str) -> Dict[str, Any]:
|
|
108
|
-
"""Delete a computer"""
|
|
109
|
-
return self._request("DELETE", f"computers/{computer_id}")
|
|
110
|
-
|
|
111
|
-
def restart_computer(self, computer_id: str) -> Dict[str, Any]:
|
|
112
|
-
"""Restart a computer"""
|
|
113
|
-
return self._request("POST", f"computers/{computer_id}/restart")
|
|
114
|
-
|
|
115
|
-
# Computer control methods
|
|
116
|
-
def left_click(self, computer_id: str, x: int, y: int) -> Dict[str, Any]:
|
|
117
|
-
return self._request("POST", f"computers/{computer_id}/click", {
|
|
118
|
-
"button": "left", "x": x, "y": y
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
def right_click(self, computer_id: str, x: int, y: int) -> Dict[str, Any]:
|
|
122
|
-
return self._request("POST", f"computers/{computer_id}/click", {
|
|
123
|
-
"button": "right", "x": x, "y": y
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
def double_click(self, computer_id: str, x: int, y: int) -> Dict[str, Any]:
|
|
127
|
-
return self._request("POST", f"computers/{computer_id}/click", {
|
|
128
|
-
"button": "left", "x": x, "y": y, "double": True
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
def drag(self, computer_id: str, start_x: int, start_y: int,
|
|
132
|
-
end_x: int, end_y: int, button: str = "left",
|
|
133
|
-
duration: float = 0.5) -> Dict[str, Any]:
|
|
134
|
-
"""Perform a drag operation from start to end coordinates"""
|
|
135
|
-
return self._request("POST", f"computers/{computer_id}/drag", {
|
|
136
|
-
"start_x": start_x,
|
|
137
|
-
"start_y": start_y,
|
|
138
|
-
"end_x": end_x,
|
|
139
|
-
"end_y": end_y,
|
|
140
|
-
"button": button,
|
|
141
|
-
"duration": duration
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
def scroll(self, computer_id: str, direction: str, amount: int = 3) -> Dict[str, Any]:
|
|
145
|
-
return self._request("POST", f"computers/{computer_id}/scroll", {
|
|
146
|
-
"direction": direction, "amount": amount
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
def type_text(self, computer_id: str, text: str) -> Dict[str, Any]:
|
|
150
|
-
return self._request("POST", f"computers/{computer_id}/type", {
|
|
151
|
-
"text": text
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
def key_press(self, computer_id: str, key: str) -> Dict[str, Any]:
|
|
155
|
-
return self._request("POST", f"computers/{computer_id}/key", {
|
|
156
|
-
"key": key
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
def get_screenshot(self, computer_id: str) -> Dict[str, Any]:
|
|
160
|
-
return self._request("GET", f"computers/{computer_id}/screenshot")
|
|
161
|
-
|
|
162
|
-
def execute_bash(self, computer_id: str, command: str) -> Dict[str, Any]:
|
|
163
|
-
return self._request("POST", f"computers/{computer_id}/bash", {
|
|
164
|
-
"command": command
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
def execute_python(self, computer_id: str, code: str, timeout: int = 10) -> Dict[str, Any]:
|
|
168
|
-
"""Execute Python code on the computer"""
|
|
169
|
-
return self._request("POST", f"computers/{computer_id}/exec", {
|
|
170
|
-
"code": code,
|
|
171
|
-
"timeout": timeout
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
def wait(self, computer_id: str, duration: float) -> Dict[str, Any]:
|
|
175
|
-
return self._request("POST", f"computers/{computer_id}/wait", {
|
|
176
|
-
"duration": duration
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
# Streaming methods
|
|
180
|
-
def start_stream(self, computer_id: str, connection_name: str) -> Dict[str, Any]:
|
|
181
|
-
"""Start streaming to a configured RTMP connection"""
|
|
182
|
-
return self._request("POST", f"computers/{computer_id}/stream/start", {
|
|
183
|
-
"connection_name": connection_name
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
def stop_stream(self, computer_id: str) -> Dict[str, Any]:
|
|
187
|
-
"""Stop the active stream"""
|
|
188
|
-
return self._request("POST", f"computers/{computer_id}/stream/stop")
|
|
189
|
-
|
|
190
|
-
def get_stream_status(self, computer_id: str) -> Dict[str, Any]:
|
|
191
|
-
"""Get current stream status"""
|
|
192
|
-
return self._request("GET", f"computers/{computer_id}/stream/status")
|
|
193
|
-
|
|
194
|
-
# Build methods
|
|
195
|
-
def create_build(self, org_id: str, project_id: str, name: Optional[str] = None) -> Dict[str, Any]:
|
|
196
|
-
"""Register a new build"""
|
|
197
|
-
data = {
|
|
198
|
-
"orgId": org_id,
|
|
199
|
-
"projectId": project_id
|
|
200
|
-
}
|
|
201
|
-
if name:
|
|
202
|
-
data["name"] = name
|
|
203
|
-
|
|
204
|
-
return self._request("POST", "builds/create", data)
|
|
205
|
-
|
|
206
|
-
def get_latest_build(self, org_id: str, project_id: str, name: str) -> Dict[str, Any]:
|
|
207
|
-
"""Get latest completed build by name"""
|
|
208
|
-
return self._request("GET", "builds/latest", {
|
|
209
|
-
"orgId": org_id,
|
|
210
|
-
"projectId": project_id,
|
|
211
|
-
"name": name
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
def update_build(self, build_id: str, status: str,
|
|
215
|
-
error_message: Optional[str] = None,
|
|
216
|
-
build_log: Optional[str] = None) -> Dict[str, Any]:
|
|
217
|
-
"""Update build status"""
|
|
218
|
-
data = {
|
|
219
|
-
"buildId": build_id,
|
|
220
|
-
"status": status
|
|
221
|
-
}
|
|
222
|
-
if error_message:
|
|
223
|
-
data["errorMessage"] = error_message
|
|
224
|
-
if build_log:
|
|
225
|
-
data["buildLog"] = build_log
|
|
226
|
-
|
|
227
|
-
return self._request("POST", "builds/update", data)
|
|
1
|
+
"""API client for Orgo service"""
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
from typing import Dict, Any, Optional, List
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
from orgo.utils.auth import get_api_key
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
class ApiClient:
|
|
12
|
+
def __init__(self, api_key: Optional[str] = None, base_url: Optional[str] = None):
|
|
13
|
+
self.api_key = get_api_key(api_key)
|
|
14
|
+
self.base_url = base_url or "https://www.orgo.ai/api"
|
|
15
|
+
self.session = requests.Session()
|
|
16
|
+
self.session.headers.update({
|
|
17
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
18
|
+
"Content-Type": "application/json",
|
|
19
|
+
"Accept": "application/json"
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
def _request(self, method: str, endpoint: str, data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
23
|
+
url = f"{self.base_url}/{endpoint}"
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
if method.upper() == "GET":
|
|
27
|
+
response = self.session.get(url, params=data)
|
|
28
|
+
else:
|
|
29
|
+
response = self.session.request(method, url, json=data)
|
|
30
|
+
|
|
31
|
+
# Handle 405 specifically for better debugging
|
|
32
|
+
if response.status_code == 405:
|
|
33
|
+
logger.error(f"Method Not Allowed: {method} {url}")
|
|
34
|
+
logger.error(f"Response: {response.text}")
|
|
35
|
+
|
|
36
|
+
response.raise_for_status()
|
|
37
|
+
return response.json()
|
|
38
|
+
except requests.exceptions.RequestException as e:
|
|
39
|
+
# Log the full error for debugging
|
|
40
|
+
logger.debug(f"API request failed: {method} {url}", exc_info=True)
|
|
41
|
+
|
|
42
|
+
if hasattr(e, 'response') and e.response is not None:
|
|
43
|
+
try:
|
|
44
|
+
error_data = e.response.json()
|
|
45
|
+
if 'error' in error_data:
|
|
46
|
+
raise Exception(error_data['error']) from None
|
|
47
|
+
except (ValueError, KeyError):
|
|
48
|
+
pass
|
|
49
|
+
raise Exception(f"Request failed with status {e.response.status_code}") from None
|
|
50
|
+
|
|
51
|
+
# Generic error message without exposing internal details
|
|
52
|
+
raise Exception("Failed to connect to Orgo service. Please check your connection and try again.") from None
|
|
53
|
+
|
|
54
|
+
# Project methods
|
|
55
|
+
def create_project(self, name: str) -> Dict[str, Any]:
|
|
56
|
+
"""Create a new named project"""
|
|
57
|
+
return self._request("POST", "projects", {"name": name})
|
|
58
|
+
|
|
59
|
+
def get_project_by_name(self, name: str) -> Dict[str, Any]:
|
|
60
|
+
"""Get project details by name"""
|
|
61
|
+
projects = self.list_projects()
|
|
62
|
+
for project in projects:
|
|
63
|
+
if project.get("name") == name:
|
|
64
|
+
return project
|
|
65
|
+
raise Exception(f"Project '{name}' not found") from None
|
|
66
|
+
|
|
67
|
+
def get_project(self, project_id: str) -> Dict[str, Any]:
|
|
68
|
+
"""Get project details by ID"""
|
|
69
|
+
return self._request("GET", f"projects/{project_id}")
|
|
70
|
+
|
|
71
|
+
def list_projects(self) -> List[Dict[str, Any]]:
|
|
72
|
+
"""List all projects"""
|
|
73
|
+
response = self._request("GET", "projects")
|
|
74
|
+
return response.get("projects", [])
|
|
75
|
+
|
|
76
|
+
def delete_project(self, project_id: str) -> Dict[str, Any]:
|
|
77
|
+
"""Delete a project and all its computers"""
|
|
78
|
+
return self._request("DELETE", f"projects/{project_id}")
|
|
79
|
+
|
|
80
|
+
# Computer methods
|
|
81
|
+
def create_computer(self, project_id: str, computer_name: str,
|
|
82
|
+
os: str = "linux", ram: int = 2, cpu: int = 2,
|
|
83
|
+
gpu: str = "none", image: Optional[str] = None) -> Dict[str, Any]:
|
|
84
|
+
"""Create a new computer within a workspace/project"""
|
|
85
|
+
data = {
|
|
86
|
+
"workspace_id": project_id, # API accepts both workspace_id and project_id
|
|
87
|
+
"name": computer_name,
|
|
88
|
+
"os": os,
|
|
89
|
+
"ram": ram,
|
|
90
|
+
"cpu": cpu,
|
|
91
|
+
"gpu": gpu
|
|
92
|
+
}
|
|
93
|
+
if image:
|
|
94
|
+
data["image"] = image
|
|
95
|
+
|
|
96
|
+
return self._request("POST", "computers", data)
|
|
97
|
+
|
|
98
|
+
def list_computers(self, project_id: str) -> List[Dict[str, Any]]:
|
|
99
|
+
"""List all computers in a project"""
|
|
100
|
+
project = self.get_project(project_id)
|
|
101
|
+
return project.get("desktops", [])
|
|
102
|
+
|
|
103
|
+
def get_computer(self, computer_id: str) -> Dict[str, Any]:
|
|
104
|
+
"""Get computer details"""
|
|
105
|
+
return self._request("GET", f"computers/{computer_id}")
|
|
106
|
+
|
|
107
|
+
def delete_computer(self, computer_id: str) -> Dict[str, Any]:
|
|
108
|
+
"""Delete a computer"""
|
|
109
|
+
return self._request("DELETE", f"computers/{computer_id}")
|
|
110
|
+
|
|
111
|
+
def restart_computer(self, computer_id: str) -> Dict[str, Any]:
|
|
112
|
+
"""Restart a computer"""
|
|
113
|
+
return self._request("POST", f"computers/{computer_id}/restart")
|
|
114
|
+
|
|
115
|
+
# Computer control methods
|
|
116
|
+
def left_click(self, computer_id: str, x: int, y: int) -> Dict[str, Any]:
|
|
117
|
+
return self._request("POST", f"computers/{computer_id}/click", {
|
|
118
|
+
"button": "left", "x": x, "y": y
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
def right_click(self, computer_id: str, x: int, y: int) -> Dict[str, Any]:
|
|
122
|
+
return self._request("POST", f"computers/{computer_id}/click", {
|
|
123
|
+
"button": "right", "x": x, "y": y
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
def double_click(self, computer_id: str, x: int, y: int) -> Dict[str, Any]:
|
|
127
|
+
return self._request("POST", f"computers/{computer_id}/click", {
|
|
128
|
+
"button": "left", "x": x, "y": y, "double": True
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
def drag(self, computer_id: str, start_x: int, start_y: int,
|
|
132
|
+
end_x: int, end_y: int, button: str = "left",
|
|
133
|
+
duration: float = 0.5) -> Dict[str, Any]:
|
|
134
|
+
"""Perform a drag operation from start to end coordinates"""
|
|
135
|
+
return self._request("POST", f"computers/{computer_id}/drag", {
|
|
136
|
+
"start_x": start_x,
|
|
137
|
+
"start_y": start_y,
|
|
138
|
+
"end_x": end_x,
|
|
139
|
+
"end_y": end_y,
|
|
140
|
+
"button": button,
|
|
141
|
+
"duration": duration
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
def scroll(self, computer_id: str, direction: str, amount: int = 3) -> Dict[str, Any]:
|
|
145
|
+
return self._request("POST", f"computers/{computer_id}/scroll", {
|
|
146
|
+
"direction": direction, "amount": amount
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
def type_text(self, computer_id: str, text: str) -> Dict[str, Any]:
|
|
150
|
+
return self._request("POST", f"computers/{computer_id}/type", {
|
|
151
|
+
"text": text
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
def key_press(self, computer_id: str, key: str) -> Dict[str, Any]:
|
|
155
|
+
return self._request("POST", f"computers/{computer_id}/key", {
|
|
156
|
+
"key": key
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
def get_screenshot(self, computer_id: str) -> Dict[str, Any]:
|
|
160
|
+
return self._request("GET", f"computers/{computer_id}/screenshot")
|
|
161
|
+
|
|
162
|
+
def execute_bash(self, computer_id: str, command: str) -> Dict[str, Any]:
|
|
163
|
+
return self._request("POST", f"computers/{computer_id}/bash", {
|
|
164
|
+
"command": command
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
def execute_python(self, computer_id: str, code: str, timeout: int = 10) -> Dict[str, Any]:
|
|
168
|
+
"""Execute Python code on the computer"""
|
|
169
|
+
return self._request("POST", f"computers/{computer_id}/exec", {
|
|
170
|
+
"code": code,
|
|
171
|
+
"timeout": timeout
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
def wait(self, computer_id: str, duration: float) -> Dict[str, Any]:
|
|
175
|
+
return self._request("POST", f"computers/{computer_id}/wait", {
|
|
176
|
+
"duration": duration
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
# Streaming methods
|
|
180
|
+
def start_stream(self, computer_id: str, connection_name: str) -> Dict[str, Any]:
|
|
181
|
+
"""Start streaming to a configured RTMP connection"""
|
|
182
|
+
return self._request("POST", f"computers/{computer_id}/stream/start", {
|
|
183
|
+
"connection_name": connection_name
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
def stop_stream(self, computer_id: str) -> Dict[str, Any]:
|
|
187
|
+
"""Stop the active stream"""
|
|
188
|
+
return self._request("POST", f"computers/{computer_id}/stream/stop")
|
|
189
|
+
|
|
190
|
+
def get_stream_status(self, computer_id: str) -> Dict[str, Any]:
|
|
191
|
+
"""Get current stream status"""
|
|
192
|
+
return self._request("GET", f"computers/{computer_id}/stream/status")
|
|
193
|
+
|
|
194
|
+
# Build methods
|
|
195
|
+
def create_build(self, org_id: str, project_id: str, name: Optional[str] = None) -> Dict[str, Any]:
|
|
196
|
+
"""Register a new build"""
|
|
197
|
+
data = {
|
|
198
|
+
"orgId": org_id,
|
|
199
|
+
"projectId": project_id
|
|
200
|
+
}
|
|
201
|
+
if name:
|
|
202
|
+
data["name"] = name
|
|
203
|
+
|
|
204
|
+
return self._request("POST", "builds/create", data)
|
|
205
|
+
|
|
206
|
+
def get_latest_build(self, org_id: str, project_id: str, name: str) -> Dict[str, Any]:
|
|
207
|
+
"""Get latest completed build by name"""
|
|
208
|
+
return self._request("GET", "builds/latest", {
|
|
209
|
+
"orgId": org_id,
|
|
210
|
+
"projectId": project_id,
|
|
211
|
+
"name": name
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
def update_build(self, build_id: str, status: str,
|
|
215
|
+
error_message: Optional[str] = None,
|
|
216
|
+
build_log: Optional[str] = None) -> Dict[str, Any]:
|
|
217
|
+
"""Update build status"""
|
|
218
|
+
data = {
|
|
219
|
+
"buildId": build_id,
|
|
220
|
+
"status": status
|
|
221
|
+
}
|
|
222
|
+
if error_message:
|
|
223
|
+
data["errorMessage"] = error_message
|
|
224
|
+
if build_log:
|
|
225
|
+
data["buildLog"] = build_log
|
|
226
|
+
|
|
227
|
+
return self._request("POST", "builds/update", data)
|