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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: orgo
3
- Version: 0.0.37
3
+ Version: 0.0.38
4
4
  Summary: Computers for AI agents
5
5
  Author: Orgo Team
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "orgo"
7
- version = "0.0.37"
7
+ version = "0.0.38"
8
8
  description = "Computers for AI agents"
9
9
  authors = [{name = "Orgo Team"}]
10
10
  license = {text = "MIT"}
@@ -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(computer_id="your-computer-id")
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
- # Connect to existing computer
33
- computer = Computer(computer_id="abc123")
53
+ # Create computer in new/existing project
54
+ computer = Computer(project="my-project")
34
55
 
35
- # Create new computer in project
36
- computer = Computer(project="my-project", ram=4, cpu=2)
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
- computer_id: Connect to existing computer by ID
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
- logger.info("Building image from Forge object...")
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
- logger.info(f"Connected to computer: {self.computer_id}")
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
- if computer_name:
121
- existing = next((c for c in computers if c.get("name") == computer_name), None)
122
- if existing:
123
- self._connect_to_existing_computer(existing)
124
- else:
125
- self._create_computer(self.project_id, computer_name)
126
- elif computers:
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
- logger.info(f"Creating new project: {project_name}")
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
- self._create_computer(self.project_id, computer_name)
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
- computers = project.list_computers()
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
- if computer_name:
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
- self._create_computer(self.project_id, computer_name)
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
- logger.info(f"Connected to: {self.name} ({self.computer_id})")
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: Optional[str]):
163
- if not computer_name:
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
- logger.warning(f"Failed to resolve image: {e}")
228
+ if self.verbose:
229
+ logger.warning(f"Failed to resolve image: {e}")
191
230
 
192
- computer = self.api.create_computer(
193
- project_id=project_id,
194
- computer_name=computer_name,
195
- os=self.os,
196
- ram=self.ram,
197
- cpu=self.cpu,
198
- gpu=self.gpu,
199
- image=image_ref
200
- )
201
- self.computer_id = computer.get("id")
202
- logger.info(f"Created: {self.name} ({self.computer_id})")
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
- return self.api.restart_computer(self.computer_id)
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
- return self.api.delete_computer(self.computer_id)
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: orgo
3
- Version: 0.0.37
3
+ Version: 0.0.38
4
4
  Summary: Computers for AI agents
5
5
  Author: Orgo Team
6
6
  License: MIT
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