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.
Files changed (96) hide show
  1. use_computer-0.0.26/README.md → use_computer-0.0.28/PKG-INFO +101 -2
  2. use_computer-0.0.26/PKG-INFO → use_computer-0.0.28/README.md +56 -24
  3. {use_computer-0.0.26 → use_computer-0.0.28}/pyproject.toml +31 -2
  4. use_computer-0.0.28/use_computer/accessibility.py +3 -0
  5. use_computer-0.0.28/use_computer/agents/__init__.py +58 -0
  6. use_computer-0.0.28/use_computer/agents/base/__init__.py +31 -0
  7. use_computer-0.0.28/use_computer/agents/base/actions.py +234 -0
  8. use_computer-0.0.28/use_computer/agents/base/agent.py +263 -0
  9. use_computer-0.0.28/use_computer/agents/base/artifacts.py +90 -0
  10. use_computer-0.0.28/use_computer/agents/base/desktop.py +126 -0
  11. use_computer-0.0.28/use_computer/agents/base/final_state.py +138 -0
  12. use_computer-0.0.28/use_computer/agents/base/prompts.py +44 -0
  13. use_computer-0.0.28/use_computer/agents/base/recordings.py +48 -0
  14. use_computer-0.0.28/use_computer/agents/base/sandbox.py +11 -0
  15. use_computer-0.0.28/use_computer/agents/base/screenshots.py +52 -0
  16. use_computer-0.0.28/use_computer/agents/base/trajectory.py +34 -0
  17. use_computer-0.0.28/use_computer/agents/core.py +65 -0
  18. use_computer-0.0.28/use_computer/agents/debug/README.md +10 -0
  19. use_computer-0.0.28/use_computer/agents/debug/__init__.py +5 -0
  20. use_computer-0.0.28/use_computer/agents/debug/actions.py +147 -0
  21. use_computer-0.0.28/use_computer/agents/debug/agent.py +263 -0
  22. use_computer-0.0.28/use_computer/agents/debug/models.py +88 -0
  23. use_computer-0.0.28/use_computer/agents/ios/__init__.py +1 -0
  24. use_computer-0.0.28/use_computer/agents/ios/agent.py +341 -0
  25. use_computer-0.0.28/use_computer/agents/ios/tools.py +367 -0
  26. use_computer-0.0.28/use_computer/agents/prompts/anthropic.txt +7 -0
  27. use_computer-0.0.28/use_computer/agents/prompts/gemini.txt +6 -0
  28. use_computer-0.0.28/use_computer/agents/prompts/ios.txt +49 -0
  29. use_computer-0.0.28/use_computer/agents/prompts/openai.txt +7 -0
  30. use_computer-0.0.28/use_computer/agents/prompts/pyautogui.txt +50 -0
  31. use_computer-0.0.28/use_computer/agents/providers/__init__.py +1 -0
  32. use_computer-0.0.28/use_computer/agents/providers/anthropic.py +290 -0
  33. use_computer-0.0.28/use_computer/agents/providers/gemini.py +402 -0
  34. use_computer-0.0.28/use_computer/agents/providers/generic.py +627 -0
  35. use_computer-0.0.28/use_computer/agents/providers/openai.py +319 -0
  36. use_computer-0.0.28/use_computer/agents/types.py +192 -0
  37. {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/client.py +38 -18
  38. use_computer-0.0.28/use_computer/display.py +3 -0
  39. use_computer-0.0.28/use_computer/harbor/__init__.py +1 -0
  40. use_computer-0.0.28/use_computer/harbor/agents.py +138 -0
  41. use_computer-0.0.28/use_computer/harbor/environment/__init__.py +17 -0
  42. use_computer-0.0.28/use_computer/harbor/environment/gateway.py +25 -0
  43. use_computer-0.0.28/use_computer/harbor/environment/platforms/__init__.py +205 -0
  44. use_computer-0.0.28/use_computer/harbor/environment/platforms/ios.py +105 -0
  45. use_computer-0.0.28/use_computer/harbor/environment/platforms/macos.py +293 -0
  46. use_computer-0.0.28/use_computer/harbor/environment/platforms/ubuntu.py +149 -0
  47. use_computer-0.0.28/use_computer/harbor/environment/platforms/windows.py +207 -0
  48. use_computer-0.0.28/use_computer/harbor/environment/setup_files.py +55 -0
  49. use_computer-0.0.28/use_computer/harbor/environment/use_computer.py +236 -0
  50. use_computer-0.0.28/use_computer/harbor/environment/utils/__init__.py +15 -0
  51. use_computer-0.0.28/use_computer/harbor/environment/utils/verifier.py +36 -0
  52. {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/parsers.py +2 -3
  53. use_computer-0.0.28/use_computer/recording.py +3 -0
  54. use_computer-0.0.28/use_computer/resources/__init__.py +17 -0
  55. {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/sandbox.py +10 -4
  56. use_computer-0.0.28/use_computer/screenshot.py +3 -0
  57. {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/tasks/__init__.py +55 -10
  58. use_computer-0.0.28/use_computer/tasks/templates/pre_command.ps1 +1 -0
  59. {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/tasks/templates/test_ios.sh +2 -2
  60. use_computer-0.0.28/use_computer/tasks/templates/test_ubuntu.sh +19 -0
  61. use_computer-0.0.28/use_computer/tasks/templates/test_ubuntu_check.sh +10 -0
  62. use_computer-0.0.28/use_computer/tasks/templates/test_ubuntu_nograder.sh +6 -0
  63. use_computer-0.0.28/use_computer/tasks/templates/test_windows.sh +19 -0
  64. use_computer-0.0.28/use_computer/tasks/templates/test_windows_check.sh +10 -0
  65. use_computer-0.0.28/use_computer/tasks/templates/test_windows_nograder.sh +6 -0
  66. use_computer-0.0.28/uv.lock +5529 -0
  67. use_computer-0.0.26/uv.lock +0 -941
  68. {use_computer-0.0.26 → use_computer-0.0.28}/.env.example +0 -0
  69. {use_computer-0.0.26 → use_computer-0.0.28}/.gitignore +0 -0
  70. {use_computer-0.0.26 → use_computer-0.0.28}/.pre-commit-config.yaml +0 -0
  71. {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/__init__.py +0 -0
  72. {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/ax_transpile.py +0 -0
  73. {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/errors.py +0 -0
  74. {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/ios/__init__.py +0 -0
  75. {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/ios/apps.py +0 -0
  76. {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/ios/environment.py +0 -0
  77. {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/ios/input.py +0 -0
  78. {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/macos/__init__.py +0 -0
  79. {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/macos/keyboard.py +0 -0
  80. {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/macos/mouse.py +0 -0
  81. {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/models.py +0 -0
  82. {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/py.typed +0 -0
  83. {use_computer-0.0.26/use_computer → use_computer-0.0.28/use_computer/resources}/accessibility.py +0 -0
  84. {use_computer-0.0.26/use_computer → use_computer-0.0.28/use_computer/resources}/display.py +0 -0
  85. {use_computer-0.0.26/use_computer → use_computer-0.0.28/use_computer/resources}/recording.py +0 -0
  86. {use_computer-0.0.26/use_computer → use_computer-0.0.28/use_computer/resources}/screenshot.py +0 -0
  87. {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/retry.py +0 -0
  88. {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/simulators.py +0 -0
  89. {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/tasks/templates/pre_command.sh +0 -0
  90. {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/tasks/templates/task.toml +0 -0
  91. {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/tasks/templates/test_ios_nograder.sh +0 -0
  92. {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/tasks/templates/test_macos.sh +0 -0
  93. {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/tasks/templates/test_macos_check.sh +0 -0
  94. {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/tasks/templates/test_macos_nograder.sh +0 -0
  95. {use_computer-0.0.26 → use_computer-0.0.28}/use_computer/windows/__init__.py +0 -0
  96. {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 a specific image. Exec
52
- defaults to bash and the macOS accessibility tree is not available yet.
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 a specific image. Exec
74
- defaults to bash and the macOS accessibility tree is not available yet.
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.26"
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
- "/use_computer/test_*.py",
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,3 @@
1
+ from use_computer.resources.accessibility import Accessibility, AsyncAccessibility
2
+
3
+ __all__ = ["Accessibility", "AsyncAccessibility"]
@@ -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)