orgo 0.0.37__tar.gz → 0.0.38__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.
- {orgo-0.0.37 → orgo-0.0.38}/PKG-INFO +1 -1
- {orgo-0.0.37 → orgo-0.0.38}/pyproject.toml +1 -1
- {orgo-0.0.37 → orgo-0.0.38}/src/orgo/computer.py +119 -56
- {orgo-0.0.37 → orgo-0.0.38}/src/orgo.egg-info/PKG-INFO +1 -1
- {orgo-0.0.37 → orgo-0.0.38}/README.md +0 -0
- {orgo-0.0.37 → orgo-0.0.38}/setup.cfg +0 -0
- {orgo-0.0.37 → orgo-0.0.38}/src/orgo/__init__.py +0 -0
- {orgo-0.0.37 → orgo-0.0.38}/src/orgo/api/__init__.py +0 -0
- {orgo-0.0.37 → orgo-0.0.38}/src/orgo/api/client.py +0 -0
- {orgo-0.0.37 → orgo-0.0.38}/src/orgo/forge.py +0 -0
- {orgo-0.0.37 → orgo-0.0.38}/src/orgo/project.py +0 -0
- {orgo-0.0.37 → orgo-0.0.38}/src/orgo/prompt.py +0 -0
- {orgo-0.0.37 → orgo-0.0.38}/src/orgo/utils/__init__.py +0 -0
- {orgo-0.0.37 → orgo-0.0.38}/src/orgo/utils/auth.py +0 -0
- {orgo-0.0.37 → orgo-0.0.38}/src/orgo.egg-info/SOURCES.txt +0 -0
- {orgo-0.0.37 → orgo-0.0.38}/src/orgo.egg-info/dependency_links.txt +0 -0
- {orgo-0.0.37 → orgo-0.0.38}/src/orgo.egg-info/requires.txt +0 -0
- {orgo-0.0.37 → orgo-0.0.38}/src/orgo.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ Orgo Computer - Control virtual computers with AI.
|
|
|
4
4
|
Usage:
|
|
5
5
|
from orgo import Computer
|
|
6
6
|
|
|
7
|
-
computer = Computer(
|
|
7
|
+
computer = Computer(project="your-project")
|
|
8
8
|
computer.prompt("Open Firefox and search for AI news")
|
|
9
9
|
"""
|
|
10
10
|
|
|
@@ -13,6 +13,7 @@ import base64
|
|
|
13
13
|
import logging
|
|
14
14
|
import uuid
|
|
15
15
|
import io
|
|
16
|
+
import random
|
|
16
17
|
from typing import Dict, List, Any, Optional, Callable, Literal, Union
|
|
17
18
|
from PIL import Image
|
|
18
19
|
import requests
|
|
@@ -24,16 +25,39 @@ from .prompt import get_provider
|
|
|
24
25
|
logger = logging.getLogger(__name__)
|
|
25
26
|
|
|
26
27
|
|
|
28
|
+
def _generate_computer_name() -> str:
|
|
29
|
+
"""Generate a random computer name like 'computer-1568'"""
|
|
30
|
+
return f"computer-{random.randint(1000, 9999)}"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _print_success(message: str):
|
|
34
|
+
"""Print a success message with nice formatting"""
|
|
35
|
+
print(f"✓ {message}")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _print_error(message: str):
|
|
39
|
+
"""Print an error message with nice formatting"""
|
|
40
|
+
print(f"✗ {message}")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _print_info(message: str):
|
|
44
|
+
"""Print an info message with nice formatting"""
|
|
45
|
+
print(f"→ {message}")
|
|
46
|
+
|
|
47
|
+
|
|
27
48
|
class Computer:
|
|
28
49
|
"""
|
|
29
50
|
Control an Orgo virtual computer.
|
|
30
51
|
|
|
31
52
|
Examples:
|
|
32
|
-
#
|
|
33
|
-
computer = Computer(
|
|
53
|
+
# Create computer in new/existing project
|
|
54
|
+
computer = Computer(project="my-project")
|
|
34
55
|
|
|
35
|
-
# Create
|
|
36
|
-
computer = Computer(project="my-project",
|
|
56
|
+
# Create with specific name
|
|
57
|
+
computer = Computer(project="my-project", name="dev-machine")
|
|
58
|
+
|
|
59
|
+
# Connect to existing computer by ID
|
|
60
|
+
computer = Computer(computer_id="abc123")
|
|
37
61
|
|
|
38
62
|
# AI control (uses Orgo by default)
|
|
39
63
|
computer.prompt("Open Firefox")
|
|
@@ -53,14 +77,15 @@ class Computer:
|
|
|
53
77
|
cpu: Optional[Literal[1, 2, 4, 8, 16]] = None,
|
|
54
78
|
os: Optional[Literal["linux", "windows"]] = None,
|
|
55
79
|
gpu: Optional[Literal["none", "a10", "l40s", "a100-40gb", "a100-80gb"]] = None,
|
|
56
|
-
image: Optional[Union[str, Any]] = None
|
|
80
|
+
image: Optional[Union[str, Any]] = None,
|
|
81
|
+
verbose: bool = True):
|
|
57
82
|
"""
|
|
58
83
|
Initialize an Orgo virtual computer.
|
|
59
84
|
|
|
60
85
|
Args:
|
|
61
|
-
|
|
62
|
-
project: Project name or Project instance
|
|
86
|
+
project: Project name or Project instance (creates if doesn't exist)
|
|
63
87
|
name: Computer name (auto-generated if not provided)
|
|
88
|
+
computer_id: Connect to existing computer by ID
|
|
64
89
|
api_key: Orgo API key (defaults to ORGO_API_KEY env var)
|
|
65
90
|
base_api_url: Custom API URL
|
|
66
91
|
ram/memory: RAM in GB (1, 2, 4, 8, 16, 32, 64)
|
|
@@ -68,10 +93,12 @@ class Computer:
|
|
|
68
93
|
os: "linux" or "windows"
|
|
69
94
|
gpu: "none", "a10", "l40s", "a100-40gb", "a100-80gb"
|
|
70
95
|
image: Custom image reference or Forge object
|
|
96
|
+
verbose: Show console output (default: True)
|
|
71
97
|
"""
|
|
72
98
|
self.api_key = api_key or operating_system.environ.get("ORGO_API_KEY")
|
|
73
99
|
self.base_api_url = base_api_url
|
|
74
100
|
self.api = ApiClient(self.api_key, self.base_api_url)
|
|
101
|
+
self.verbose = verbose
|
|
75
102
|
|
|
76
103
|
if ram is None and memory is not None:
|
|
77
104
|
ram = memory
|
|
@@ -83,7 +110,8 @@ class Computer:
|
|
|
83
110
|
self.image = image
|
|
84
111
|
|
|
85
112
|
if hasattr(self.image, 'build') and callable(self.image.build):
|
|
86
|
-
|
|
113
|
+
if self.verbose:
|
|
114
|
+
_print_info("Building image from Forge object...")
|
|
87
115
|
self.image = self.image.build()
|
|
88
116
|
|
|
89
117
|
if computer_id:
|
|
@@ -91,7 +119,8 @@ class Computer:
|
|
|
91
119
|
self.name = name
|
|
92
120
|
self.project_id = None
|
|
93
121
|
self.project_name = None
|
|
94
|
-
|
|
122
|
+
if self.verbose:
|
|
123
|
+
_print_success(f"Connected to computer: {self.computer_id}")
|
|
95
124
|
elif project:
|
|
96
125
|
if isinstance(project, str):
|
|
97
126
|
self.project_name = project
|
|
@@ -112,60 +141,69 @@ class Computer:
|
|
|
112
141
|
# =========================================================================
|
|
113
142
|
|
|
114
143
|
def _initialize_with_project_name(self, project_name: str, computer_name: Optional[str]):
|
|
144
|
+
"""Initialize computer with project name (create project if needed)"""
|
|
115
145
|
try:
|
|
146
|
+
# Try to get existing project
|
|
116
147
|
project = self.api.get_project_by_name(project_name)
|
|
117
148
|
self.project_id = project.get("id")
|
|
118
|
-
computers = self.api.list_computers(self.project_id)
|
|
119
149
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
self._connect_to_existing_computer(computers[0])
|
|
128
|
-
else:
|
|
129
|
-
self._create_computer(self.project_id, computer_name)
|
|
150
|
+
# If no computer name specified, generate one
|
|
151
|
+
if not computer_name:
|
|
152
|
+
computer_name = _generate_computer_name()
|
|
153
|
+
|
|
154
|
+
# Create the computer in this project
|
|
155
|
+
self._create_computer(self.project_id, computer_name, project_name)
|
|
156
|
+
|
|
130
157
|
except Exception:
|
|
131
|
-
|
|
158
|
+
# Project doesn't exist, create it
|
|
159
|
+
if self.verbose:
|
|
160
|
+
_print_info(f"Creating project: {project_name}")
|
|
132
161
|
project = self.api.create_project(project_name)
|
|
133
162
|
self.project_id = project.get("id")
|
|
134
|
-
|
|
163
|
+
|
|
164
|
+
# Generate name if not specified
|
|
165
|
+
if not computer_name:
|
|
166
|
+
computer_name = _generate_computer_name()
|
|
167
|
+
|
|
168
|
+
self._create_computer(self.project_id, computer_name, project_name)
|
|
135
169
|
|
|
136
170
|
def _initialize_with_project_instance(self, project: 'Project', computer_name: Optional[str]):
|
|
137
|
-
|
|
171
|
+
"""Initialize computer with Project instance"""
|
|
172
|
+
# Generate name if not specified
|
|
173
|
+
if not computer_name:
|
|
174
|
+
computer_name = _generate_computer_name()
|
|
138
175
|
|
|
139
|
-
|
|
140
|
-
existing = next((c for c in computers if c.get("name") == computer_name), None)
|
|
141
|
-
if existing:
|
|
142
|
-
self._connect_to_existing_computer(existing)
|
|
143
|
-
else:
|
|
144
|
-
self._create_computer(project.id, computer_name)
|
|
145
|
-
elif computers:
|
|
146
|
-
self._connect_to_existing_computer(computers[0])
|
|
147
|
-
else:
|
|
148
|
-
self._create_computer(project.id, computer_name)
|
|
176
|
+
self._create_computer(project.id, computer_name, project.name)
|
|
149
177
|
|
|
150
178
|
def _create_new_project_and_computer(self, computer_name: Optional[str]):
|
|
179
|
+
"""Create a new project and computer when no project specified"""
|
|
151
180
|
project_name = f"project-{uuid.uuid4().hex[:8]}"
|
|
181
|
+
|
|
182
|
+
if self.verbose:
|
|
183
|
+
_print_info(f"Creating project: {project_name}")
|
|
184
|
+
|
|
152
185
|
project = self.api.create_project(project_name)
|
|
153
186
|
self.project_id = project.get("id")
|
|
154
187
|
self.project_name = project_name
|
|
155
|
-
|
|
188
|
+
|
|
189
|
+
# Generate name if not specified
|
|
190
|
+
if not computer_name:
|
|
191
|
+
computer_name = _generate_computer_name()
|
|
192
|
+
|
|
193
|
+
self._create_computer(self.project_id, computer_name, project_name)
|
|
156
194
|
|
|
157
195
|
def _connect_to_existing_computer(self, computer_info: Dict[str, Any]):
|
|
196
|
+
"""Connect to an existing computer"""
|
|
158
197
|
self.computer_id = computer_info.get("id")
|
|
159
198
|
self.name = computer_info.get("name")
|
|
160
|
-
|
|
199
|
+
if self.verbose:
|
|
200
|
+
_print_success(f"Connected to: {self.name} ({self.computer_id})")
|
|
161
201
|
|
|
162
|
-
def _create_computer(self, project_id: str, computer_name:
|
|
163
|
-
|
|
164
|
-
computer_name = f"desktop-{uuid.uuid4().hex[:8]}"
|
|
165
|
-
|
|
202
|
+
def _create_computer(self, project_id: str, computer_name: str, project_name: str):
|
|
203
|
+
"""Create a new computer with beautiful console output"""
|
|
166
204
|
self.name = computer_name
|
|
167
205
|
|
|
168
|
-
# Validate
|
|
206
|
+
# Validate parameters
|
|
169
207
|
if self.ram not in [1, 2, 4, 8, 16, 32, 64]:
|
|
170
208
|
raise ValueError("ram must be: 1, 2, 4, 8, 16, 32, or 64 GB")
|
|
171
209
|
if self.cpu not in [1, 2, 4, 8, 16]:
|
|
@@ -175,7 +213,7 @@ class Computer:
|
|
|
175
213
|
if self.gpu not in ["none", "a10", "l40s", "a100-40gb", "a100-80gb"]:
|
|
176
214
|
raise ValueError("gpu must be: 'none', 'a10', 'l40s', 'a100-40gb', or 'a100-80gb'")
|
|
177
215
|
|
|
178
|
-
# Resolve image
|
|
216
|
+
# Resolve image if needed
|
|
179
217
|
image_ref = self.image
|
|
180
218
|
if image_ref and isinstance(image_ref, str) and not image_ref.startswith("registry.fly.io"):
|
|
181
219
|
try:
|
|
@@ -187,19 +225,34 @@ class Computer:
|
|
|
187
225
|
if resolved:
|
|
188
226
|
image_ref = resolved
|
|
189
227
|
except Exception as e:
|
|
190
|
-
|
|
228
|
+
if self.verbose:
|
|
229
|
+
logger.warning(f"Failed to resolve image: {e}")
|
|
191
230
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
231
|
+
# Create the computer
|
|
232
|
+
try:
|
|
233
|
+
computer = self.api.create_computer(
|
|
234
|
+
project_id=project_id,
|
|
235
|
+
computer_name=computer_name,
|
|
236
|
+
os=self.os,
|
|
237
|
+
ram=self.ram,
|
|
238
|
+
cpu=self.cpu,
|
|
239
|
+
gpu=self.gpu,
|
|
240
|
+
image=image_ref
|
|
241
|
+
)
|
|
242
|
+
self.computer_id = computer.get("id")
|
|
243
|
+
|
|
244
|
+
# Beautiful success message
|
|
245
|
+
if self.verbose:
|
|
246
|
+
_print_success(
|
|
247
|
+
f"Computer [{self.name}] successfully created under workspace [{project_name}]"
|
|
248
|
+
)
|
|
249
|
+
_print_info(f"ID: {self.computer_id}")
|
|
250
|
+
_print_info(f"View at: https://orgo.ai/workspaces/{self.computer_id}")
|
|
251
|
+
|
|
252
|
+
except Exception as e:
|
|
253
|
+
if self.verbose:
|
|
254
|
+
_print_error(f"Failed to create computer: {str(e)}")
|
|
255
|
+
raise
|
|
203
256
|
|
|
204
257
|
# =========================================================================
|
|
205
258
|
# Computer Management
|
|
@@ -211,11 +264,21 @@ class Computer:
|
|
|
211
264
|
|
|
212
265
|
def restart(self) -> Dict[str, Any]:
|
|
213
266
|
"""Restart the computer."""
|
|
214
|
-
|
|
267
|
+
if self.verbose:
|
|
268
|
+
_print_info(f"Restarting computer: {self.name}")
|
|
269
|
+
result = self.api.restart_computer(self.computer_id)
|
|
270
|
+
if self.verbose:
|
|
271
|
+
_print_success("Computer restarted")
|
|
272
|
+
return result
|
|
215
273
|
|
|
216
274
|
def destroy(self) -> Dict[str, Any]:
|
|
217
275
|
"""Delete the computer."""
|
|
218
|
-
|
|
276
|
+
if self.verbose:
|
|
277
|
+
_print_info(f"Deleting computer: {self.name}")
|
|
278
|
+
result = self.api.delete_computer(self.computer_id)
|
|
279
|
+
if self.verbose:
|
|
280
|
+
_print_success("Computer deleted")
|
|
281
|
+
return result
|
|
219
282
|
|
|
220
283
|
# =========================================================================
|
|
221
284
|
# Mouse Actions
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|