use-computer 0.0.26__tar.gz → 0.0.28__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.
- use_computer-0.0.26/README.md → use_computer-0.0.28/PKG-INFO +101 -2
- use_computer-0.0.26/PKG-INFO → use_computer-0.0.28/README.md +56 -24
- {use_computer-0.0.26 → use_computer-0.0.28}/pyproject.toml +31 -2
- use_computer-0.0.28/use_computer/accessibility.py +3 -0
- use_computer-0.0.28/use_computer/agents/__init__.py +58 -0
- use_computer-0.0.28/use_computer/agents/base/__init__.py +31 -0
- use_computer-0.0.28/use_computer/agents/base/actions.py +234 -0
- use_computer-0.0.28/use_computer/agents/base/agent.py +263 -0
- use_computer-0.0.28/use_computer/agents/base/artifacts.py +90 -0
- use_computer-0.0.28/use_computer/agents/base/desktop.py +126 -0
- use_computer-0.0.28/use_computer/agents/base/final_state.py +138 -0
- use_computer-0.0.28/use_computer/agents/base/prompts.py +44 -0
- use_computer-0.0.28/use_computer/agents/base/recordings.py +48 -0
- use_computer-0.0.28/use_computer/agents/base/sandbox.py +11 -0
- use_computer-0.0.28/use_computer/agents/base/screenshots.py +52 -0
- use_computer-0.0.28/use_computer/agents/base/trajectory.py +34 -0
- use_computer-0.0.28/use_computer/agents/core.py +65 -0
- use_computer-0.0.28/use_computer/agents/debug/README.md +10 -0
- use_computer-0.0.28/use_computer/agents/debug/__init__.py +5 -0
- use_computer-0.0.28/use_computer/agents/debug/actions.py +147 -0
- use_computer-0.0.28/use_computer/agents/debug/agent.py +263 -0
- use_computer-0.0.28/use_computer/agents/debug/models.py +88 -0
- use_computer-0.0.28/use_computer/agents/ios/__init__.py +1 -0
- use_computer-0.0.28/use_computer/agents/ios/agent.py +341 -0
- use_computer-0.0.28/use_computer/agents/ios/tools.py +367 -0
- use_computer-0.0.28/use_computer/agents/prompts/anthropic.txt +7 -0
- use_computer-0.0.28/use_computer/agents/prompts/gemini.txt +6 -0
- use_computer-0.0.28/use_computer/agents/prompts/ios.txt +49 -0
- use_computer-0.0.28/use_computer/agents/prompts/openai.txt +7 -0
- use_computer-0.0.28/use_computer/agents/prompts/pyautogui.txt +50 -0
- use_computer-0.0.28/use_computer/agents/providers/__init__.py +1 -0
- use_computer-0.0.28/use_computer/agents/providers/anthropic.py +290 -0
- use_computer-0.0.28/use_computer/agents/providers/gemini.py +402 -0
- use_computer-0.0.28/use_computer/agents/providers/generic.py +627 -0
- use_computer-0.0.28/use_computer/agents/providers/openai.py +319 -0
- use_computer-0.0.28/use_computer/agents/types.py +192 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/client.py +38 -18
- use_computer-0.0.28/use_computer/display.py +3 -0
- use_computer-0.0.28/use_computer/harbor/__init__.py +1 -0
- use_computer-0.0.28/use_computer/harbor/agents.py +138 -0
- use_computer-0.0.28/use_computer/harbor/environment/__init__.py +17 -0
- use_computer-0.0.28/use_computer/harbor/environment/gateway.py +25 -0
- use_computer-0.0.28/use_computer/harbor/environment/platforms/__init__.py +205 -0
- use_computer-0.0.28/use_computer/harbor/environment/platforms/ios.py +105 -0
- use_computer-0.0.28/use_computer/harbor/environment/platforms/macos.py +293 -0
- use_computer-0.0.28/use_computer/harbor/environment/platforms/ubuntu.py +149 -0
- use_computer-0.0.28/use_computer/harbor/environment/platforms/windows.py +207 -0
- use_computer-0.0.28/use_computer/harbor/environment/setup_files.py +55 -0
- use_computer-0.0.28/use_computer/harbor/environment/use_computer.py +236 -0
- use_computer-0.0.28/use_computer/harbor/environment/utils/__init__.py +15 -0
- use_computer-0.0.28/use_computer/harbor/environment/utils/verifier.py +36 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/parsers.py +2 -3
- use_computer-0.0.28/use_computer/recording.py +3 -0
- use_computer-0.0.28/use_computer/resources/__init__.py +17 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/sandbox.py +10 -4
- use_computer-0.0.28/use_computer/screenshot.py +3 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/tasks/__init__.py +55 -10
- use_computer-0.0.28/use_computer/tasks/templates/pre_command.ps1 +1 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/tasks/templates/test_ios.sh +2 -2
- use_computer-0.0.28/use_computer/tasks/templates/test_ubuntu.sh +19 -0
- use_computer-0.0.28/use_computer/tasks/templates/test_ubuntu_check.sh +10 -0
- use_computer-0.0.28/use_computer/tasks/templates/test_ubuntu_nograder.sh +6 -0
- use_computer-0.0.28/use_computer/tasks/templates/test_windows.sh +19 -0
- use_computer-0.0.28/use_computer/tasks/templates/test_windows_check.sh +10 -0
- use_computer-0.0.28/use_computer/tasks/templates/test_windows_nograder.sh +6 -0
- use_computer-0.0.28/uv.lock +5529 -0
- use_computer-0.0.26/uv.lock +0 -941
- {use_computer-0.0.26 → use_computer-0.0.28}/.env.example +0 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/.gitignore +0 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/.pre-commit-config.yaml +0 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/__init__.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/ax_transpile.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/errors.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/ios/__init__.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/ios/apps.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/ios/environment.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/ios/input.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/macos/__init__.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/macos/keyboard.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/macos/mouse.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/models.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/py.typed +0 -0
- {use_computer-0.0.26/use_computer → use_computer-0.0.28/use_computer/resources}/accessibility.py +0 -0
- {use_computer-0.0.26/use_computer → use_computer-0.0.28/use_computer/resources}/display.py +0 -0
- {use_computer-0.0.26/use_computer → use_computer-0.0.28/use_computer/resources}/recording.py +0 -0
- {use_computer-0.0.26/use_computer → use_computer-0.0.28/use_computer/resources}/screenshot.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/retry.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/simulators.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/tasks/templates/pre_command.sh +0 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/tasks/templates/task.toml +0 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/tasks/templates/test_ios_nograder.sh +0 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/tasks/templates/test_macos.sh +0 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/tasks/templates/test_macos_check.sh +0 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/tasks/templates/test_macos_nograder.sh +0 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/windows/__init__.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/windows/shell.py +0 -0
|
@@ -1,3 +1,48 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: use-computer
|
|
3
|
+
Version: 0.0.28
|
|
4
|
+
Summary: Python SDK for use.computer sandboxes across macOS, iOS simulators, Windows, and Ubuntu
|
|
5
|
+
Project-URL: Homepage, https://use.computer
|
|
6
|
+
Project-URL: Documentation, https://api.use.computer/docs
|
|
7
|
+
Project-URL: Repository, https://github.com/josancamon19/use-computer-python
|
|
8
|
+
Author: use.computer
|
|
9
|
+
Keywords: automation,computer-use,ios,macos,sandbox,ubuntu,vnc,windows
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Requires-Dist: httpx>=0.27
|
|
21
|
+
Provides-Extra: agents
|
|
22
|
+
Requires-Dist: pillow>=10; extra == 'agents'
|
|
23
|
+
Provides-Extra: agents-all
|
|
24
|
+
Requires-Dist: anthropic>=0.86; extra == 'agents-all'
|
|
25
|
+
Requires-Dist: google-genai>=1; extra == 'agents-all'
|
|
26
|
+
Requires-Dist: litellm>=1; extra == 'agents-all'
|
|
27
|
+
Requires-Dist: openai>=1; extra == 'agents-all'
|
|
28
|
+
Requires-Dist: pillow>=10; extra == 'agents-all'
|
|
29
|
+
Provides-Extra: agents-anthropic
|
|
30
|
+
Requires-Dist: anthropic>=0.86; extra == 'agents-anthropic'
|
|
31
|
+
Requires-Dist: pillow>=10; extra == 'agents-anthropic'
|
|
32
|
+
Provides-Extra: agents-gemini
|
|
33
|
+
Requires-Dist: google-genai>=1; extra == 'agents-gemini'
|
|
34
|
+
Requires-Dist: pillow>=10; extra == 'agents-gemini'
|
|
35
|
+
Provides-Extra: agents-litellm
|
|
36
|
+
Requires-Dist: litellm>=1; extra == 'agents-litellm'
|
|
37
|
+
Requires-Dist: pillow>=10; extra == 'agents-litellm'
|
|
38
|
+
Provides-Extra: agents-openai
|
|
39
|
+
Requires-Dist: openai>=1; extra == 'agents-openai'
|
|
40
|
+
Requires-Dist: pillow>=10; extra == 'agents-openai'
|
|
41
|
+
Provides-Extra: harbor
|
|
42
|
+
Requires-Dist: pyyaml>=6; extra == 'harbor'
|
|
43
|
+
Requires-Dist: tomli>=2; extra == 'harbor'
|
|
44
|
+
Description-Content-Type: text/markdown
|
|
45
|
+
|
|
1
46
|
# use-computer Python SDK
|
|
2
47
|
|
|
3
48
|
Python client for [use.computer](https://use.computer) — rent dedicated VMs across macOS, iOS/visionOS/tvOS simulators, Windows, and Ubuntu built for computer-use agents.
|
|
@@ -7,6 +52,14 @@ pip install use-computer
|
|
|
7
52
|
export USE_COMPUTER_API_KEY=mk_live_...
|
|
8
53
|
```
|
|
9
54
|
|
|
55
|
+
Optional agent and Harbor integrations are installed explicitly:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pip install "use-computer[agents-openai]" # or agents-anthropic / agents-gemini / agents-litellm / agents-all
|
|
59
|
+
pip install "use-computer[harbor]" # SDK Harbor adapter deps only; install Harbor separately until it is published
|
|
60
|
+
pip install "use-computer[harbor,agents-all]" # Harbor adapter plus all provider deps
|
|
61
|
+
```
|
|
62
|
+
|
|
10
63
|
```python
|
|
11
64
|
from use_computer import Computer, SandboxType, SimulatorFamily
|
|
12
65
|
|
|
@@ -38,18 +91,62 @@ recording/file surface as macOS; exec runs in-guest via PowerShell/cmd
|
|
|
38
91
|
(`win.run` / `win.shell`) instead of SSH. `AsyncWindowsSandbox` mirrors it.
|
|
39
92
|
See [docs.use.computer/docs/windows](https://docs.use.computer/docs/windows).
|
|
40
93
|
|
|
94
|
+
Selectable Windows images are exposed by `Computer().platforms()["windows"]`.
|
|
95
|
+
Prefer `version="windows-11"` plus `resources={"cpus": 4, "memory_mb": 4096,
|
|
96
|
+
"disk_gb": 40}` to select a resource profile. Explicit image IDs such as
|
|
97
|
+
`windows-11-4c4g40g` still work. The image metadata includes CPU/RAM/disk and
|
|
98
|
+
display size.
|
|
99
|
+
|
|
41
100
|
### Ubuntu (Beta)
|
|
42
101
|
|
|
43
102
|
```python
|
|
44
103
|
with Computer().create(type="ubuntu", version="ubuntu-24.04") as ubuntu:
|
|
45
104
|
print(ubuntu.run("uname -a").stdout) # bash exec (no SSH)
|
|
105
|
+
print(ubuntu.display.get_info())
|
|
46
106
|
ubuntu.keyboard.type("hello")
|
|
47
107
|
ubuntu.screenshot.take_full_screen()
|
|
48
108
|
```
|
|
49
109
|
|
|
50
110
|
Ubuntu sandboxes are **Beta** (admin-only). They use the same KVM/noVNC backend
|
|
51
|
-
as Windows; pass `version="ubuntu-24.04"` to select
|
|
52
|
-
|
|
111
|
+
as Windows; pass `version="ubuntu-24.04"` plus `resources` to select CPU/RAM/disk.
|
|
112
|
+
|
|
113
|
+
Selectable Ubuntu images are exposed by `Computer().platforms()["ubuntu"]`.
|
|
114
|
+
Current examples include `ubuntu-24.04`, `ubuntu-24.04-4c4g80g`,
|
|
115
|
+
`ubuntu-24.04-4c4g40g`, and `ubuntu-24.04-2c4g40g`.
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
client = Computer()
|
|
119
|
+
platforms = client.platforms()
|
|
120
|
+
for image in platforms["ubuntu"]["images"]:
|
|
121
|
+
print(image["version"], image["resources"], image["display"])
|
|
122
|
+
|
|
123
|
+
with client.create(
|
|
124
|
+
type="ubuntu",
|
|
125
|
+
version="ubuntu-24.04",
|
|
126
|
+
resources={"cpus": 2, "memory_mb": 4096, "disk_gb": 40},
|
|
127
|
+
) as ubuntu:
|
|
128
|
+
print(ubuntu.display.get_info())
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Snapshots (Beta)
|
|
132
|
+
|
|
133
|
+
Windows and Ubuntu snapshots preserve disk + RAM state, including open apps and
|
|
134
|
+
running processes. Seed a VM once, snapshot it, then create new sandboxes from
|
|
135
|
+
that snapshot version.
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
client = Computer()
|
|
139
|
+
|
|
140
|
+
with client.create(type="ubuntu", version="ubuntu-24.04") as ubuntu:
|
|
141
|
+
ubuntu.run("google-chrome-stable --no-first-run https://example.com >/tmp/chrome.log 2>&1 &")
|
|
142
|
+
snapshot = ubuntu.snapshot("chrome-seeded-ubuntu")
|
|
143
|
+
|
|
144
|
+
with client.create(type="ubuntu", snapshot=snapshot.version) as seeded:
|
|
145
|
+
print(seeded.run("pgrep -af 'chrome|chromium' | head").stdout)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Use `client.snapshots("ubuntu")` or `client.snapshots("windows")` to list saved
|
|
149
|
+
snapshot versions.
|
|
53
150
|
|
|
54
151
|
Full DSL reference: [docs.use.computer/docs/sdk](https://docs.use.computer/docs/sdk)
|
|
55
152
|
|
|
@@ -85,6 +182,8 @@ visionOS yet. Use it for read-only flows for now.
|
|
|
85
182
|
| [`examples/_6_hello_tvos.py`](examples/_6_hello_tvos.py) | tvOS: pick TV family + drive the Apple Remote |
|
|
86
183
|
| [`examples/_7_hello_windows.py`](examples/_7_hello_windows.py) | create Windows → run PowerShell → screenshot |
|
|
87
184
|
| [`examples/_8_hello_ubuntu.py`](examples/_8_hello_ubuntu.py) | create Ubuntu → run bash → screenshot |
|
|
185
|
+
| [`examples/_9_seeding.py`](examples/_9_seeding.py) | typed setup: files, hosts, open URLs/apps |
|
|
186
|
+
| [`examples/_10_snapshots.py`](examples/_10_snapshots.py) | snapshot seeded Ubuntu/Windows desktop state |
|
|
88
187
|
|
|
89
188
|
For agent loops and evals: [use-computer-cookbook](https://github.com/josancamon19/use-computer-cookbook).
|
|
90
189
|
|
|
@@ -1,25 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: use-computer
|
|
3
|
-
Version: 0.0.26
|
|
4
|
-
Summary: Python SDK for use.computer sandboxes across macOS, iOS simulators, Windows, and Ubuntu
|
|
5
|
-
Project-URL: Homepage, https://use.computer
|
|
6
|
-
Project-URL: Documentation, https://api.use.computer/docs
|
|
7
|
-
Project-URL: Repository, https://github.com/josancamon19/use-computer-python
|
|
8
|
-
Author: use.computer
|
|
9
|
-
Keywords: automation,computer-use,ios,macos,sandbox,ubuntu,vnc,windows
|
|
10
|
-
Classifier: Development Status :: 3 - Alpha
|
|
11
|
-
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: Operating System :: OS Independent
|
|
13
|
-
Classifier: Programming Language :: Python :: 3
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
-
Classifier: Typing :: Typed
|
|
19
|
-
Requires-Python: >=3.10
|
|
20
|
-
Requires-Dist: httpx>=0.27
|
|
21
|
-
Description-Content-Type: text/markdown
|
|
22
|
-
|
|
23
1
|
# use-computer Python SDK
|
|
24
2
|
|
|
25
3
|
Python client for [use.computer](https://use.computer) — rent dedicated VMs across macOS, iOS/visionOS/tvOS simulators, Windows, and Ubuntu built for computer-use agents.
|
|
@@ -29,6 +7,14 @@ pip install use-computer
|
|
|
29
7
|
export USE_COMPUTER_API_KEY=mk_live_...
|
|
30
8
|
```
|
|
31
9
|
|
|
10
|
+
Optional agent and Harbor integrations are installed explicitly:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install "use-computer[agents-openai]" # or agents-anthropic / agents-gemini / agents-litellm / agents-all
|
|
14
|
+
pip install "use-computer[harbor]" # SDK Harbor adapter deps only; install Harbor separately until it is published
|
|
15
|
+
pip install "use-computer[harbor,agents-all]" # Harbor adapter plus all provider deps
|
|
16
|
+
```
|
|
17
|
+
|
|
32
18
|
```python
|
|
33
19
|
from use_computer import Computer, SandboxType, SimulatorFamily
|
|
34
20
|
|
|
@@ -60,18 +46,62 @@ recording/file surface as macOS; exec runs in-guest via PowerShell/cmd
|
|
|
60
46
|
(`win.run` / `win.shell`) instead of SSH. `AsyncWindowsSandbox` mirrors it.
|
|
61
47
|
See [docs.use.computer/docs/windows](https://docs.use.computer/docs/windows).
|
|
62
48
|
|
|
49
|
+
Selectable Windows images are exposed by `Computer().platforms()["windows"]`.
|
|
50
|
+
Prefer `version="windows-11"` plus `resources={"cpus": 4, "memory_mb": 4096,
|
|
51
|
+
"disk_gb": 40}` to select a resource profile. Explicit image IDs such as
|
|
52
|
+
`windows-11-4c4g40g` still work. The image metadata includes CPU/RAM/disk and
|
|
53
|
+
display size.
|
|
54
|
+
|
|
63
55
|
### Ubuntu (Beta)
|
|
64
56
|
|
|
65
57
|
```python
|
|
66
58
|
with Computer().create(type="ubuntu", version="ubuntu-24.04") as ubuntu:
|
|
67
59
|
print(ubuntu.run("uname -a").stdout) # bash exec (no SSH)
|
|
60
|
+
print(ubuntu.display.get_info())
|
|
68
61
|
ubuntu.keyboard.type("hello")
|
|
69
62
|
ubuntu.screenshot.take_full_screen()
|
|
70
63
|
```
|
|
71
64
|
|
|
72
65
|
Ubuntu sandboxes are **Beta** (admin-only). They use the same KVM/noVNC backend
|
|
73
|
-
as Windows; pass `version="ubuntu-24.04"` to select
|
|
74
|
-
|
|
66
|
+
as Windows; pass `version="ubuntu-24.04"` plus `resources` to select CPU/RAM/disk.
|
|
67
|
+
|
|
68
|
+
Selectable Ubuntu images are exposed by `Computer().platforms()["ubuntu"]`.
|
|
69
|
+
Current examples include `ubuntu-24.04`, `ubuntu-24.04-4c4g80g`,
|
|
70
|
+
`ubuntu-24.04-4c4g40g`, and `ubuntu-24.04-2c4g40g`.
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
client = Computer()
|
|
74
|
+
platforms = client.platforms()
|
|
75
|
+
for image in platforms["ubuntu"]["images"]:
|
|
76
|
+
print(image["version"], image["resources"], image["display"])
|
|
77
|
+
|
|
78
|
+
with client.create(
|
|
79
|
+
type="ubuntu",
|
|
80
|
+
version="ubuntu-24.04",
|
|
81
|
+
resources={"cpus": 2, "memory_mb": 4096, "disk_gb": 40},
|
|
82
|
+
) as ubuntu:
|
|
83
|
+
print(ubuntu.display.get_info())
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Snapshots (Beta)
|
|
87
|
+
|
|
88
|
+
Windows and Ubuntu snapshots preserve disk + RAM state, including open apps and
|
|
89
|
+
running processes. Seed a VM once, snapshot it, then create new sandboxes from
|
|
90
|
+
that snapshot version.
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
client = Computer()
|
|
94
|
+
|
|
95
|
+
with client.create(type="ubuntu", version="ubuntu-24.04") as ubuntu:
|
|
96
|
+
ubuntu.run("google-chrome-stable --no-first-run https://example.com >/tmp/chrome.log 2>&1 &")
|
|
97
|
+
snapshot = ubuntu.snapshot("chrome-seeded-ubuntu")
|
|
98
|
+
|
|
99
|
+
with client.create(type="ubuntu", snapshot=snapshot.version) as seeded:
|
|
100
|
+
print(seeded.run("pgrep -af 'chrome|chromium' | head").stdout)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Use `client.snapshots("ubuntu")` or `client.snapshots("windows")` to list saved
|
|
104
|
+
snapshot versions.
|
|
75
105
|
|
|
76
106
|
Full DSL reference: [docs.use.computer/docs/sdk](https://docs.use.computer/docs/sdk)
|
|
77
107
|
|
|
@@ -107,6 +137,8 @@ visionOS yet. Use it for read-only flows for now.
|
|
|
107
137
|
| [`examples/_6_hello_tvos.py`](examples/_6_hello_tvos.py) | tvOS: pick TV family + drive the Apple Remote |
|
|
108
138
|
| [`examples/_7_hello_windows.py`](examples/_7_hello_windows.py) | create Windows → run PowerShell → screenshot |
|
|
109
139
|
| [`examples/_8_hello_ubuntu.py`](examples/_8_hello_ubuntu.py) | create Ubuntu → run bash → screenshot |
|
|
140
|
+
| [`examples/_9_seeding.py`](examples/_9_seeding.py) | typed setup: files, hosts, open URLs/apps |
|
|
141
|
+
| [`examples/_10_snapshots.py`](examples/_10_snapshots.py) | snapshot seeded Ubuntu/Windows desktop state |
|
|
110
142
|
|
|
111
143
|
For agent loops and evals: [use-computer-cookbook](https://github.com/josancamon19/use-computer-cookbook).
|
|
112
144
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "use-computer"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.28"
|
|
8
8
|
description = "Python SDK for use.computer sandboxes across macOS, iOS simulators, Windows, and Ubuntu"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -23,11 +23,37 @@ classifiers = [
|
|
|
23
23
|
"Typing :: Typed",
|
|
24
24
|
]
|
|
25
25
|
|
|
26
|
+
[project.optional-dependencies]
|
|
27
|
+
agents = ["Pillow>=10"]
|
|
28
|
+
agents-openai = ["Pillow>=10", "openai>=1"]
|
|
29
|
+
agents-anthropic = ["Pillow>=10", "anthropic>=0.86"]
|
|
30
|
+
agents-litellm = ["Pillow>=10", "litellm>=1"]
|
|
31
|
+
agents-gemini = ["Pillow>=10", "google-genai>=1"]
|
|
32
|
+
agents-all = [
|
|
33
|
+
"Pillow>=10",
|
|
34
|
+
"openai>=1",
|
|
35
|
+
"anthropic>=0.86",
|
|
36
|
+
"litellm>=1",
|
|
37
|
+
"google-genai>=1",
|
|
38
|
+
]
|
|
39
|
+
harbor = [
|
|
40
|
+
"PyYAML>=6",
|
|
41
|
+
"tomli>=2",
|
|
42
|
+
]
|
|
43
|
+
|
|
26
44
|
[dependency-groups]
|
|
27
45
|
dev = [
|
|
46
|
+
"anthropic>=0.86",
|
|
47
|
+
"google-genai>=1",
|
|
48
|
+
"harbor @ git+https://github.com/josancamon19/harbor.git@mirror ; python_version >= '3.12'",
|
|
49
|
+
"litellm>=1",
|
|
50
|
+
"openai>=1",
|
|
28
51
|
"pre-commit>=4",
|
|
29
52
|
"pytest>=9",
|
|
30
53
|
"ruff>=0.15",
|
|
54
|
+
"Pillow>=10",
|
|
55
|
+
"PyYAML>=6",
|
|
56
|
+
"tomli>=2",
|
|
31
57
|
"twine>=6",
|
|
32
58
|
"ty>=0.0.34",
|
|
33
59
|
]
|
|
@@ -48,9 +74,12 @@ exclude = [
|
|
|
48
74
|
"/.venv",
|
|
49
75
|
"/dist",
|
|
50
76
|
"/results",
|
|
51
|
-
"/
|
|
77
|
+
"/tests",
|
|
52
78
|
]
|
|
53
79
|
|
|
80
|
+
[tool.pytest.ini_options]
|
|
81
|
+
testpaths = ["tests"]
|
|
82
|
+
|
|
54
83
|
[tool.ruff]
|
|
55
84
|
target-version = "py310"
|
|
56
85
|
line-length = 100
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Optional computer-use agents.
|
|
2
|
+
|
|
3
|
+
Install extras for provider-backed agents:
|
|
4
|
+
use-computer[agents-openai], use-computer[agents-anthropic],
|
|
5
|
+
use-computer[agents-litellm], use-computer[agents-gemini], or
|
|
6
|
+
use-computer[agents-all].
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from importlib import import_module
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from use_computer.agents.core import AgentRunOptions, AgentRunResult, AgentUsage
|
|
15
|
+
|
|
16
|
+
_LAZY_EXPORTS = {
|
|
17
|
+
"AnthropicComputerAgent": (
|
|
18
|
+
"use_computer.agents.providers.anthropic",
|
|
19
|
+
"AnthropicComputerAgent",
|
|
20
|
+
),
|
|
21
|
+
"AnthropicCUAAgent": ("use_computer.agents.providers.anthropic", "AnthropicCUAAgent"),
|
|
22
|
+
"DebugComputerAgent": ("use_computer.agents.debug.agent", "DebugComputerAgent"),
|
|
23
|
+
"DebugCUAAgent": ("use_computer.agents.debug.agent", "DebugCUAAgent"),
|
|
24
|
+
"GeminiComputerAgent": ("use_computer.agents.providers.gemini", "GeminiComputerAgent"),
|
|
25
|
+
"GeminiCUAAgent": ("use_computer.agents.providers.gemini", "GeminiCUAAgent"),
|
|
26
|
+
"GenericComputerAgent": ("use_computer.agents.providers.generic", "GenericComputerAgent"),
|
|
27
|
+
"GenericCUAAgent": ("use_computer.agents.providers.generic", "GenericCUAAgent"),
|
|
28
|
+
"IOSAgent": ("use_computer.agents.ios.agent", "IOSAgent"),
|
|
29
|
+
"IOSComputerAgent": ("use_computer.agents.ios.agent", "IOSComputerAgent"),
|
|
30
|
+
"OpenAIComputerAgent": ("use_computer.agents.providers.openai", "OpenAIComputerAgent"),
|
|
31
|
+
"OpenAICUAAgent": ("use_computer.agents.providers.openai", "OpenAICUAAgent"),
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
__all__ = [
|
|
35
|
+
"AgentRunOptions",
|
|
36
|
+
"AgentRunResult",
|
|
37
|
+
"AgentUsage",
|
|
38
|
+
*_LAZY_EXPORTS,
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def __getattr__(name: str) -> Any:
|
|
43
|
+
target = _LAZY_EXPORTS.get(name)
|
|
44
|
+
if target is None:
|
|
45
|
+
raise AttributeError(name)
|
|
46
|
+
module_name, attr = target
|
|
47
|
+
try:
|
|
48
|
+
module = import_module(module_name)
|
|
49
|
+
except ModuleNotFoundError as exc:
|
|
50
|
+
raise ModuleNotFoundError(
|
|
51
|
+
f"{name} requires an optional agent extra. Install one of "
|
|
52
|
+
"`use-computer[agents]`, `use-computer[agents-openai]`, "
|
|
53
|
+
"`use-computer[agents-anthropic]`, `use-computer[agents-litellm]`, "
|
|
54
|
+
"`use-computer[agents-gemini]`, or `use-computer[agents-all]`."
|
|
55
|
+
) from exc
|
|
56
|
+
value = getattr(module, attr)
|
|
57
|
+
globals()[name] = value
|
|
58
|
+
return value
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Public base-agent surface for CUA agents."""
|
|
2
|
+
|
|
3
|
+
from use_computer.agents.base.actions import _annotate_click, execute_action
|
|
4
|
+
from use_computer.agents.base.agent import BaseCUAAgent
|
|
5
|
+
from use_computer.agents.base.desktop import (
|
|
6
|
+
DesktopProfile,
|
|
7
|
+
OpenAIComputerEnvironment,
|
|
8
|
+
desktop_profile,
|
|
9
|
+
)
|
|
10
|
+
from use_computer.agents.base.prompts import build_system_prompt, load_prompt
|
|
11
|
+
from use_computer.agents.base.sandbox import ComputerSandbox
|
|
12
|
+
from use_computer.agents.base.screenshots import (
|
|
13
|
+
scale_screenshot_for_model,
|
|
14
|
+
screenshot_cap_for_model,
|
|
15
|
+
)
|
|
16
|
+
from use_computer.agents.base.trajectory import write_trajectory
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"BaseCUAAgent",
|
|
20
|
+
"ComputerSandbox",
|
|
21
|
+
"DesktopProfile",
|
|
22
|
+
"OpenAIComputerEnvironment",
|
|
23
|
+
"_annotate_click",
|
|
24
|
+
"build_system_prompt",
|
|
25
|
+
"desktop_profile",
|
|
26
|
+
"execute_action",
|
|
27
|
+
"load_prompt",
|
|
28
|
+
"scale_screenshot_for_model",
|
|
29
|
+
"screenshot_cap_for_model",
|
|
30
|
+
"write_trajectory",
|
|
31
|
+
]
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"""Desktop action execution for CUA agents."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import io
|
|
7
|
+
import logging
|
|
8
|
+
import time
|
|
9
|
+
from collections.abc import Mapping
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
import httpx
|
|
14
|
+
from PIL import Image, ImageDraw
|
|
15
|
+
|
|
16
|
+
from use_computer.agents.base.desktop import normalize_key
|
|
17
|
+
from use_computer.agents.base.sandbox import ComputerSandbox
|
|
18
|
+
from use_computer.agents.types import DesktopAction, DesktopCommand, MouseButton
|
|
19
|
+
|
|
20
|
+
_log = logging.getLogger("use_computer.agent")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _extract_coords(
|
|
24
|
+
action: dict[str, Any],
|
|
25
|
+
) -> tuple[tuple[int, int] | None, tuple[int, int] | None]:
|
|
26
|
+
"""Pull (point, secondary_point) from any action shape we use."""
|
|
27
|
+
point: tuple[int, int] | None = None
|
|
28
|
+
secondary: tuple[int, int] | None = None
|
|
29
|
+
if "coordinate" in action and action["coordinate"]:
|
|
30
|
+
c = action["coordinate"]
|
|
31
|
+
point = (int(c[0]), int(c[1]))
|
|
32
|
+
if "start_coordinate" in action and action["start_coordinate"]:
|
|
33
|
+
s = action["start_coordinate"]
|
|
34
|
+
secondary = (int(s[0]), int(s[1]))
|
|
35
|
+
if point is None and "x" in action and "y" in action:
|
|
36
|
+
try:
|
|
37
|
+
point = (int(float(action["x"])), int(float(action["y"])))
|
|
38
|
+
except (TypeError, ValueError):
|
|
39
|
+
pass
|
|
40
|
+
if "to_x" in action and "to_y" in action:
|
|
41
|
+
try:
|
|
42
|
+
point = (int(float(action["to_x"])), int(float(action["to_y"])))
|
|
43
|
+
except (TypeError, ValueError):
|
|
44
|
+
pass
|
|
45
|
+
if "from_x" in action and "from_y" in action:
|
|
46
|
+
try:
|
|
47
|
+
secondary = (int(float(action["from_x"])), int(float(action["from_y"])))
|
|
48
|
+
except (TypeError, ValueError):
|
|
49
|
+
pass
|
|
50
|
+
return point, secondary
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _annotate_click(png_bytes: bytes, action: dict[str, Any]) -> bytes:
|
|
54
|
+
"""Draw a red marker at the click/move/scroll/drag coordinate."""
|
|
55
|
+
point, start = _extract_coords(action)
|
|
56
|
+
if point is None and start is None:
|
|
57
|
+
return png_bytes
|
|
58
|
+
try:
|
|
59
|
+
img = Image.open(io.BytesIO(png_bytes)).convert("RGBA")
|
|
60
|
+
overlay = Image.new("RGBA", img.size, (0, 0, 0, 0))
|
|
61
|
+
draw = ImageDraw.Draw(overlay)
|
|
62
|
+
red = (255, 30, 30, 240)
|
|
63
|
+
radius = 5
|
|
64
|
+
if point and start:
|
|
65
|
+
draw.line([start, point], fill=red, width=2)
|
|
66
|
+
for px, py in (start, point):
|
|
67
|
+
draw.ellipse([px - radius, py - radius, px + radius, py + radius], fill=red)
|
|
68
|
+
else:
|
|
69
|
+
cx, cy = point or start # type: ignore
|
|
70
|
+
draw.ellipse([cx - radius, cy - radius, cx + radius, cy + radius], fill=red)
|
|
71
|
+
out = Image.alpha_composite(img, overlay).convert("RGB")
|
|
72
|
+
buf = io.BytesIO()
|
|
73
|
+
out.save(buf, format="PNG")
|
|
74
|
+
return buf.getvalue()
|
|
75
|
+
except Exception as e:
|
|
76
|
+
_log.warning(f"annotate_click failed: {e}")
|
|
77
|
+
return png_bytes
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
async def execute_action(
|
|
81
|
+
sandbox: ComputerSandbox,
|
|
82
|
+
action: dict[str, Any],
|
|
83
|
+
images_dir: Path,
|
|
84
|
+
step_idx: int,
|
|
85
|
+
key_aliases: Mapping[str, str] | None = None,
|
|
86
|
+
) -> tuple[str, bytes | None, str | None]:
|
|
87
|
+
"""Execute a computer-use action; returns (result_text, ss_bytes, ss_path)."""
|
|
88
|
+
command = DesktopCommand.from_mapping(action)
|
|
89
|
+
img_name = f"step_{step_idx:03d}.png"
|
|
90
|
+
t0 = time.monotonic()
|
|
91
|
+
|
|
92
|
+
if command.action is DesktopAction.SCREENSHOT:
|
|
93
|
+
ss = await _take_screenshot_with_recovery(sandbox, command.label)
|
|
94
|
+
if ss is None:
|
|
95
|
+
return "Screenshot failed after retries", None, None
|
|
96
|
+
(images_dir / img_name).write_bytes(ss)
|
|
97
|
+
_log.info(f"action screenshot: {time.monotonic() - t0:.2f}s, {len(ss)} bytes")
|
|
98
|
+
return "Screenshot taken", ss, f"images/{img_name}"
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
if command.action in {DesktopAction.LEFT_CLICK, DesktopAction.CLICK}:
|
|
102
|
+
await sandbox.mouse.click(
|
|
103
|
+
command.point.x,
|
|
104
|
+
command.point.y,
|
|
105
|
+
button=MouseButton.LEFT.value,
|
|
106
|
+
)
|
|
107
|
+
elif command.action is DesktopAction.RIGHT_CLICK:
|
|
108
|
+
await sandbox.mouse.click(
|
|
109
|
+
command.point.x,
|
|
110
|
+
command.point.y,
|
|
111
|
+
button=MouseButton.RIGHT.value,
|
|
112
|
+
)
|
|
113
|
+
elif command.action is DesktopAction.DOUBLE_CLICK:
|
|
114
|
+
await sandbox.mouse.click(
|
|
115
|
+
command.point.x,
|
|
116
|
+
command.point.y,
|
|
117
|
+
button=MouseButton.LEFT.value,
|
|
118
|
+
double=True,
|
|
119
|
+
)
|
|
120
|
+
elif command.action is DesktopAction.TRIPLE_CLICK:
|
|
121
|
+
await sandbox.mouse.click(
|
|
122
|
+
command.point.x,
|
|
123
|
+
command.point.y,
|
|
124
|
+
button=MouseButton.LEFT.value,
|
|
125
|
+
double=True,
|
|
126
|
+
)
|
|
127
|
+
await asyncio.sleep(0.05)
|
|
128
|
+
await sandbox.mouse.click(
|
|
129
|
+
command.point.x,
|
|
130
|
+
command.point.y,
|
|
131
|
+
button=MouseButton.LEFT.value,
|
|
132
|
+
)
|
|
133
|
+
elif command.action is DesktopAction.MIDDLE_CLICK:
|
|
134
|
+
await sandbox.mouse.click(
|
|
135
|
+
command.point.x,
|
|
136
|
+
command.point.y,
|
|
137
|
+
button=MouseButton.MIDDLE.value,
|
|
138
|
+
)
|
|
139
|
+
elif command.action is DesktopAction.TYPE:
|
|
140
|
+
await sandbox.keyboard.type(command.text)
|
|
141
|
+
elif command.action is DesktopAction.KEY:
|
|
142
|
+
key = normalize_key(command.key or command.text, key_aliases)
|
|
143
|
+
if "+" in key:
|
|
144
|
+
await sandbox.keyboard.hotkey(key)
|
|
145
|
+
else:
|
|
146
|
+
await sandbox.keyboard.press(key)
|
|
147
|
+
elif command.action is DesktopAction.SCROLL:
|
|
148
|
+
await sandbox.mouse.scroll(
|
|
149
|
+
command.point.x,
|
|
150
|
+
command.point.y,
|
|
151
|
+
command.direction.value,
|
|
152
|
+
command.amount,
|
|
153
|
+
)
|
|
154
|
+
elif command.action is DesktopAction.MOVE:
|
|
155
|
+
await sandbox.mouse.move(command.point.x, command.point.y)
|
|
156
|
+
elif command.action is DesktopAction.DRAG:
|
|
157
|
+
await sandbox.mouse.drag(
|
|
158
|
+
command.start_point.x,
|
|
159
|
+
command.start_point.y,
|
|
160
|
+
command.point.x,
|
|
161
|
+
command.point.y,
|
|
162
|
+
)
|
|
163
|
+
elif command.action is DesktopAction.WAIT:
|
|
164
|
+
await asyncio.sleep(command.duration)
|
|
165
|
+
else:
|
|
166
|
+
return f"Unknown action: {command.label}", None, None
|
|
167
|
+
except httpx.HTTPStatusError as e:
|
|
168
|
+
detail = e.response.text.strip().replace("\n", " ")[:240]
|
|
169
|
+
msg = f"Action '{command.label}' failed: HTTP {e.response.status_code} {detail}"
|
|
170
|
+
_log.warning(msg)
|
|
171
|
+
ss = await _take_screenshot_with_recovery(sandbox, command.label)
|
|
172
|
+
if ss is None:
|
|
173
|
+
return msg, None, None
|
|
174
|
+
(images_dir / img_name).write_bytes(ss)
|
|
175
|
+
return msg, ss, f"images/{img_name}"
|
|
176
|
+
except httpx.HTTPError as e:
|
|
177
|
+
msg = f"Action '{command.label}' failed: {type(e).__name__}: {e}"
|
|
178
|
+
_log.warning(msg)
|
|
179
|
+
ss = await _take_screenshot_with_recovery(sandbox, command.label)
|
|
180
|
+
if ss is None:
|
|
181
|
+
return msg, None, None
|
|
182
|
+
(images_dir / img_name).write_bytes(ss)
|
|
183
|
+
return msg, ss, f"images/{img_name}"
|
|
184
|
+
|
|
185
|
+
action_elapsed = time.monotonic() - t0
|
|
186
|
+
await asyncio.sleep(2)
|
|
187
|
+
ss = await _take_screenshot_with_recovery(sandbox, command.label)
|
|
188
|
+
if ss is None:
|
|
189
|
+
return (
|
|
190
|
+
f"Action '{command.label}' executed; screenshot failed after retries",
|
|
191
|
+
None,
|
|
192
|
+
None,
|
|
193
|
+
)
|
|
194
|
+
annotated = _annotate_click(ss, action)
|
|
195
|
+
(images_dir / img_name).write_bytes(annotated)
|
|
196
|
+
total_elapsed = time.monotonic() - t0
|
|
197
|
+
screenshot_elapsed = total_elapsed - action_elapsed
|
|
198
|
+
_log.info(
|
|
199
|
+
f"action {command.label}: {action_elapsed:.2f}s + "
|
|
200
|
+
f"screenshot {screenshot_elapsed:.2f}s = {total_elapsed:.2f}s total"
|
|
201
|
+
)
|
|
202
|
+
return f"Action '{command.label}' executed", ss, f"images/{img_name}"
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
async def _take_screenshot_with_recovery(
|
|
206
|
+
sandbox: ComputerSandbox, action_type: str
|
|
207
|
+
) -> bytes | None:
|
|
208
|
+
attempts = 4
|
|
209
|
+
for attempt in range(1, attempts + 1):
|
|
210
|
+
try:
|
|
211
|
+
return await sandbox.screenshot.take_full_screen()
|
|
212
|
+
except httpx.HTTPStatusError as e:
|
|
213
|
+
if attempt == attempts:
|
|
214
|
+
_log.warning(
|
|
215
|
+
f"screenshot after {action_type} failed after retries: "
|
|
216
|
+
f"HTTP {e.response.status_code} {e.response.text[:160]}"
|
|
217
|
+
)
|
|
218
|
+
return None
|
|
219
|
+
_log.warning(
|
|
220
|
+
f"screenshot after {action_type} failed: HTTP {e.response.status_code}; "
|
|
221
|
+
f"retrying in 10s ({attempt}/{attempts - 1})"
|
|
222
|
+
)
|
|
223
|
+
await asyncio.sleep(10)
|
|
224
|
+
except httpx.HTTPError as e:
|
|
225
|
+
if attempt == attempts:
|
|
226
|
+
_log.warning(
|
|
227
|
+
f"screenshot after {action_type} failed after retries: {type(e).__name__}: {e}"
|
|
228
|
+
)
|
|
229
|
+
return None
|
|
230
|
+
_log.warning(
|
|
231
|
+
f"screenshot after {action_type} failed: {type(e).__name__}; "
|
|
232
|
+
f"retrying in 10s ({attempt}/{attempts - 1})"
|
|
233
|
+
)
|
|
234
|
+
await asyncio.sleep(10)
|