use-computer 0.0.26__tar.gz → 0.0.27__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 → use_computer-0.0.27}/PKG-INFO +49 -3
- {use_computer-0.0.26 → use_computer-0.0.27}/README.md +48 -2
- {use_computer-0.0.26 → use_computer-0.0.27}/pyproject.toml +5 -2
- use_computer-0.0.27/use_computer/accessibility.py +3 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/client.py +37 -17
- use_computer-0.0.27/use_computer/display.py +3 -0
- use_computer-0.0.27/use_computer/recording.py +3 -0
- use_computer-0.0.27/use_computer/resources/__init__.py +17 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/sandbox.py +10 -4
- use_computer-0.0.27/use_computer/screenshot.py +3 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/tasks/__init__.py +55 -10
- use_computer-0.0.27/use_computer/tasks/templates/pre_command.ps1 +1 -0
- use_computer-0.0.27/use_computer/tasks/templates/test_ubuntu.sh +20 -0
- use_computer-0.0.27/use_computer/tasks/templates/test_ubuntu_check.sh +10 -0
- use_computer-0.0.27/use_computer/tasks/templates/test_ubuntu_nograder.sh +6 -0
- use_computer-0.0.27/use_computer/tasks/templates/test_windows.sh +20 -0
- use_computer-0.0.27/use_computer/tasks/templates/test_windows_check.sh +10 -0
- use_computer-0.0.27/use_computer/tasks/templates/test_windows_nograder.sh +6 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/.env.example +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/.gitignore +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/.pre-commit-config.yaml +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/__init__.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/ax_transpile.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/errors.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/ios/__init__.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/ios/apps.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/ios/environment.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/ios/input.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/macos/__init__.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/macos/keyboard.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/macos/mouse.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/models.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/parsers.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/py.typed +0 -0
- {use_computer-0.0.26/use_computer → use_computer-0.0.27/use_computer/resources}/accessibility.py +0 -0
- {use_computer-0.0.26/use_computer → use_computer-0.0.27/use_computer/resources}/display.py +0 -0
- {use_computer-0.0.26/use_computer → use_computer-0.0.27/use_computer/resources}/recording.py +0 -0
- {use_computer-0.0.26/use_computer → use_computer-0.0.27/use_computer/resources}/screenshot.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/retry.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/simulators.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/tasks/templates/pre_command.sh +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/tasks/templates/task.toml +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/tasks/templates/test_ios.sh +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/tasks/templates/test_ios_nograder.sh +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/tasks/templates/test_macos.sh +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/tasks/templates/test_macos_check.sh +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/tasks/templates/test_macos_nograder.sh +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/windows/__init__.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/use_computer/windows/shell.py +0 -0
- {use_computer-0.0.26 → use_computer-0.0.27}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: use-computer
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.27
|
|
4
4
|
Summary: Python SDK for use.computer sandboxes across macOS, iOS simulators, Windows, and Ubuntu
|
|
5
5
|
Project-URL: Homepage, https://use.computer
|
|
6
6
|
Project-URL: Documentation, https://api.use.computer/docs
|
|
@@ -60,18 +60,62 @@ recording/file surface as macOS; exec runs in-guest via PowerShell/cmd
|
|
|
60
60
|
(`win.run` / `win.shell`) instead of SSH. `AsyncWindowsSandbox` mirrors it.
|
|
61
61
|
See [docs.use.computer/docs/windows](https://docs.use.computer/docs/windows).
|
|
62
62
|
|
|
63
|
+
Selectable Windows images are exposed by `Computer().platforms()["windows"]`.
|
|
64
|
+
Prefer `version="windows-11"` plus `resources={"cpus": 4, "memory_mb": 4096,
|
|
65
|
+
"disk_gb": 40}` to select a resource profile. Explicit image IDs such as
|
|
66
|
+
`windows-11-4c4g40g` still work. The image metadata includes CPU/RAM/disk and
|
|
67
|
+
display size.
|
|
68
|
+
|
|
63
69
|
### Ubuntu (Beta)
|
|
64
70
|
|
|
65
71
|
```python
|
|
66
72
|
with Computer().create(type="ubuntu", version="ubuntu-24.04") as ubuntu:
|
|
67
73
|
print(ubuntu.run("uname -a").stdout) # bash exec (no SSH)
|
|
74
|
+
print(ubuntu.display.get_info())
|
|
68
75
|
ubuntu.keyboard.type("hello")
|
|
69
76
|
ubuntu.screenshot.take_full_screen()
|
|
70
77
|
```
|
|
71
78
|
|
|
72
79
|
Ubuntu sandboxes are **Beta** (admin-only). They use the same KVM/noVNC backend
|
|
73
|
-
as Windows; pass `version="ubuntu-24.04"` to select
|
|
74
|
-
|
|
80
|
+
as Windows; pass `version="ubuntu-24.04"` plus `resources` to select CPU/RAM/disk.
|
|
81
|
+
|
|
82
|
+
Selectable Ubuntu images are exposed by `Computer().platforms()["ubuntu"]`.
|
|
83
|
+
Current examples include `ubuntu-24.04`, `ubuntu-24.04-4c4g80g`,
|
|
84
|
+
`ubuntu-24.04-4c4g40g`, and `ubuntu-24.04-2c4g40g`.
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
client = Computer()
|
|
88
|
+
platforms = client.platforms()
|
|
89
|
+
for image in platforms["ubuntu"]["images"]:
|
|
90
|
+
print(image["version"], image["resources"], image["display"])
|
|
91
|
+
|
|
92
|
+
with client.create(
|
|
93
|
+
type="ubuntu",
|
|
94
|
+
version="ubuntu-24.04",
|
|
95
|
+
resources={"cpus": 2, "memory_mb": 4096, "disk_gb": 40},
|
|
96
|
+
) as ubuntu:
|
|
97
|
+
print(ubuntu.display.get_info())
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Snapshots (Beta)
|
|
101
|
+
|
|
102
|
+
Windows and Ubuntu snapshots preserve disk + RAM state, including open apps and
|
|
103
|
+
running processes. Seed a VM once, snapshot it, then create new sandboxes from
|
|
104
|
+
that snapshot version.
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
client = Computer()
|
|
108
|
+
|
|
109
|
+
with client.create(type="ubuntu", version="ubuntu-24.04") as ubuntu:
|
|
110
|
+
ubuntu.run("google-chrome-stable --no-first-run https://example.com >/tmp/chrome.log 2>&1 &")
|
|
111
|
+
snapshot = ubuntu.snapshot("chrome-seeded-ubuntu")
|
|
112
|
+
|
|
113
|
+
with client.create(type="ubuntu", snapshot=snapshot.version) as seeded:
|
|
114
|
+
print(seeded.run("pgrep -af 'chrome|chromium' | head").stdout)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Use `client.snapshots("ubuntu")` or `client.snapshots("windows")` to list saved
|
|
118
|
+
snapshot versions.
|
|
75
119
|
|
|
76
120
|
Full DSL reference: [docs.use.computer/docs/sdk](https://docs.use.computer/docs/sdk)
|
|
77
121
|
|
|
@@ -107,6 +151,8 @@ visionOS yet. Use it for read-only flows for now.
|
|
|
107
151
|
| [`examples/_6_hello_tvos.py`](examples/_6_hello_tvos.py) | tvOS: pick TV family + drive the Apple Remote |
|
|
108
152
|
| [`examples/_7_hello_windows.py`](examples/_7_hello_windows.py) | create Windows → run PowerShell → screenshot |
|
|
109
153
|
| [`examples/_8_hello_ubuntu.py`](examples/_8_hello_ubuntu.py) | create Ubuntu → run bash → screenshot |
|
|
154
|
+
| [`examples/_9_seeding.py`](examples/_9_seeding.py) | typed setup: files, hosts, open URLs/apps |
|
|
155
|
+
| [`examples/_10_snapshots.py`](examples/_10_snapshots.py) | snapshot seeded Ubuntu/Windows desktop state |
|
|
110
156
|
|
|
111
157
|
For agent loops and evals: [use-computer-cookbook](https://github.com/josancamon19/use-computer-cookbook).
|
|
112
158
|
|
|
@@ -38,18 +38,62 @@ recording/file surface as macOS; exec runs in-guest via PowerShell/cmd
|
|
|
38
38
|
(`win.run` / `win.shell`) instead of SSH. `AsyncWindowsSandbox` mirrors it.
|
|
39
39
|
See [docs.use.computer/docs/windows](https://docs.use.computer/docs/windows).
|
|
40
40
|
|
|
41
|
+
Selectable Windows images are exposed by `Computer().platforms()["windows"]`.
|
|
42
|
+
Prefer `version="windows-11"` plus `resources={"cpus": 4, "memory_mb": 4096,
|
|
43
|
+
"disk_gb": 40}` to select a resource profile. Explicit image IDs such as
|
|
44
|
+
`windows-11-4c4g40g` still work. The image metadata includes CPU/RAM/disk and
|
|
45
|
+
display size.
|
|
46
|
+
|
|
41
47
|
### Ubuntu (Beta)
|
|
42
48
|
|
|
43
49
|
```python
|
|
44
50
|
with Computer().create(type="ubuntu", version="ubuntu-24.04") as ubuntu:
|
|
45
51
|
print(ubuntu.run("uname -a").stdout) # bash exec (no SSH)
|
|
52
|
+
print(ubuntu.display.get_info())
|
|
46
53
|
ubuntu.keyboard.type("hello")
|
|
47
54
|
ubuntu.screenshot.take_full_screen()
|
|
48
55
|
```
|
|
49
56
|
|
|
50
57
|
Ubuntu sandboxes are **Beta** (admin-only). They use the same KVM/noVNC backend
|
|
51
|
-
as Windows; pass `version="ubuntu-24.04"` to select
|
|
52
|
-
|
|
58
|
+
as Windows; pass `version="ubuntu-24.04"` plus `resources` to select CPU/RAM/disk.
|
|
59
|
+
|
|
60
|
+
Selectable Ubuntu images are exposed by `Computer().platforms()["ubuntu"]`.
|
|
61
|
+
Current examples include `ubuntu-24.04`, `ubuntu-24.04-4c4g80g`,
|
|
62
|
+
`ubuntu-24.04-4c4g40g`, and `ubuntu-24.04-2c4g40g`.
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
client = Computer()
|
|
66
|
+
platforms = client.platforms()
|
|
67
|
+
for image in platforms["ubuntu"]["images"]:
|
|
68
|
+
print(image["version"], image["resources"], image["display"])
|
|
69
|
+
|
|
70
|
+
with client.create(
|
|
71
|
+
type="ubuntu",
|
|
72
|
+
version="ubuntu-24.04",
|
|
73
|
+
resources={"cpus": 2, "memory_mb": 4096, "disk_gb": 40},
|
|
74
|
+
) as ubuntu:
|
|
75
|
+
print(ubuntu.display.get_info())
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Snapshots (Beta)
|
|
79
|
+
|
|
80
|
+
Windows and Ubuntu snapshots preserve disk + RAM state, including open apps and
|
|
81
|
+
running processes. Seed a VM once, snapshot it, then create new sandboxes from
|
|
82
|
+
that snapshot version.
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
client = Computer()
|
|
86
|
+
|
|
87
|
+
with client.create(type="ubuntu", version="ubuntu-24.04") as ubuntu:
|
|
88
|
+
ubuntu.run("google-chrome-stable --no-first-run https://example.com >/tmp/chrome.log 2>&1 &")
|
|
89
|
+
snapshot = ubuntu.snapshot("chrome-seeded-ubuntu")
|
|
90
|
+
|
|
91
|
+
with client.create(type="ubuntu", snapshot=snapshot.version) as seeded:
|
|
92
|
+
print(seeded.run("pgrep -af 'chrome|chromium' | head").stdout)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Use `client.snapshots("ubuntu")` or `client.snapshots("windows")` to list saved
|
|
96
|
+
snapshot versions.
|
|
53
97
|
|
|
54
98
|
Full DSL reference: [docs.use.computer/docs/sdk](https://docs.use.computer/docs/sdk)
|
|
55
99
|
|
|
@@ -85,6 +129,8 @@ visionOS yet. Use it for read-only flows for now.
|
|
|
85
129
|
| [`examples/_6_hello_tvos.py`](examples/_6_hello_tvos.py) | tvOS: pick TV family + drive the Apple Remote |
|
|
86
130
|
| [`examples/_7_hello_windows.py`](examples/_7_hello_windows.py) | create Windows → run PowerShell → screenshot |
|
|
87
131
|
| [`examples/_8_hello_ubuntu.py`](examples/_8_hello_ubuntu.py) | create Ubuntu → run bash → screenshot |
|
|
132
|
+
| [`examples/_9_seeding.py`](examples/_9_seeding.py) | typed setup: files, hosts, open URLs/apps |
|
|
133
|
+
| [`examples/_10_snapshots.py`](examples/_10_snapshots.py) | snapshot seeded Ubuntu/Windows desktop state |
|
|
88
134
|
|
|
89
135
|
For agent loops and evals: [use-computer-cookbook](https://github.com/josancamon19/use-computer-cookbook).
|
|
90
136
|
|
|
@@ -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.27"
|
|
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"
|
|
@@ -48,9 +48,12 @@ exclude = [
|
|
|
48
48
|
"/.venv",
|
|
49
49
|
"/dist",
|
|
50
50
|
"/results",
|
|
51
|
-
"/
|
|
51
|
+
"/tests",
|
|
52
52
|
]
|
|
53
53
|
|
|
54
|
+
[tool.pytest.ini_options]
|
|
55
|
+
testpaths = ["tests"]
|
|
56
|
+
|
|
54
57
|
[tool.ruff]
|
|
55
58
|
target-version = "py310"
|
|
56
59
|
line-length = 100
|
|
@@ -41,6 +41,12 @@ def _resolve_base_url(base_url: str | None) -> str:
|
|
|
41
41
|
return (base_url or os.getenv("USE_COMPUTER_BASE_URL") or DEFAULT_BASE_URL).rstrip("/")
|
|
42
42
|
|
|
43
43
|
|
|
44
|
+
def _absolute_url(base_url: str, value: str) -> str:
|
|
45
|
+
if not value or value.startswith(("http://", "https://")):
|
|
46
|
+
return value
|
|
47
|
+
return f"{base_url}{'' if value.startswith('/') else '/'}{value}"
|
|
48
|
+
|
|
49
|
+
|
|
44
50
|
def _normalize_sandbox_type(type: SandboxTypeInput) -> SandboxType:
|
|
45
51
|
if isinstance(type, SandboxType):
|
|
46
52
|
return type
|
|
@@ -146,6 +152,7 @@ class Computer:
|
|
|
146
152
|
type: Literal["windows", SandboxType.WINDOWS],
|
|
147
153
|
version: str = ...,
|
|
148
154
|
snapshot: str = ...,
|
|
155
|
+
resources: dict[str, int] | None = ...,
|
|
149
156
|
host: str = ...,
|
|
150
157
|
) -> WindowsSandbox: ...
|
|
151
158
|
|
|
@@ -156,6 +163,7 @@ class Computer:
|
|
|
156
163
|
type: Literal["ubuntu", SandboxType.UBUNTU],
|
|
157
164
|
version: str = ...,
|
|
158
165
|
snapshot: str = ...,
|
|
166
|
+
resources: dict[str, int] | None = ...,
|
|
159
167
|
host: str = ...,
|
|
160
168
|
) -> UbuntuSandbox: ...
|
|
161
169
|
|
|
@@ -169,6 +177,7 @@ class Computer:
|
|
|
169
177
|
runtime: str = "",
|
|
170
178
|
version: str = "",
|
|
171
179
|
snapshot: str = "",
|
|
180
|
+
resources: dict[str, int] | None = None,
|
|
172
181
|
) -> MacOSSandbox | IOSSandbox | WindowsSandbox | UbuntuSandbox:
|
|
173
182
|
"""Create a new sandbox.
|
|
174
183
|
|
|
@@ -181,6 +190,8 @@ class Computer:
|
|
|
181
190
|
version: KVM desktop image version, for example "windows-11",
|
|
182
191
|
"windows-10", or "ubuntu-24.04".
|
|
183
192
|
snapshot: Windur snapshot version returned by sandbox.snapshot().
|
|
193
|
+
resources: KVM CPU/RAM/disk request used to select the baked image
|
|
194
|
+
profile, for example {"cpus": 4, "memory_mb": 4096, "disk_gb": 40}.
|
|
184
195
|
"""
|
|
185
196
|
sandbox_type = _normalize_sandbox_type(type)
|
|
186
197
|
body: dict = {"type": sandbox_type.value}
|
|
@@ -193,6 +204,8 @@ class Computer:
|
|
|
193
204
|
body["snapshot"] = snapshot
|
|
194
205
|
elif version:
|
|
195
206
|
body["version"] = version
|
|
207
|
+
if resources:
|
|
208
|
+
body["resources"] = resources
|
|
196
209
|
if sandbox_type == SandboxType.IOS:
|
|
197
210
|
if family and (not device_type or not runtime):
|
|
198
211
|
choice = select_simulator(self.platforms(), family)
|
|
@@ -214,7 +227,7 @@ class Computer:
|
|
|
214
227
|
return WindowsSandbox(
|
|
215
228
|
sandbox_id=sid,
|
|
216
229
|
http=self._http,
|
|
217
|
-
vnc_url=
|
|
230
|
+
vnc_url=_absolute_url(self._base_url, data.get("vnc_url", "")),
|
|
218
231
|
host=data.get("host", ""),
|
|
219
232
|
version=data.get("version", ""),
|
|
220
233
|
)
|
|
@@ -222,17 +235,17 @@ class Computer:
|
|
|
222
235
|
return UbuntuSandbox(
|
|
223
236
|
sandbox_id=sid,
|
|
224
237
|
http=self._http,
|
|
225
|
-
vnc_url=
|
|
238
|
+
vnc_url=_absolute_url(self._base_url, data.get("vnc_url", "")),
|
|
226
239
|
host=data.get("host", ""),
|
|
227
240
|
version=data.get("version", ""),
|
|
228
241
|
)
|
|
229
242
|
return MacOSSandbox(
|
|
230
243
|
sandbox_id=sid,
|
|
231
244
|
http=self._http,
|
|
232
|
-
vnc_url=
|
|
245
|
+
vnc_url=_absolute_url(self._base_url, data.get("vnc_url", "")),
|
|
233
246
|
vm_ip=data.get("vm_ip", ""),
|
|
234
247
|
host=data.get("host", ""),
|
|
235
|
-
ssh_url=
|
|
248
|
+
ssh_url=_absolute_url(self._base_url, data.get("ssh_url", "")),
|
|
236
249
|
)
|
|
237
250
|
|
|
238
251
|
def get(self, sandbox_id: str) -> Sandbox:
|
|
@@ -247,7 +260,7 @@ class Computer:
|
|
|
247
260
|
return WindowsSandbox(
|
|
248
261
|
sandbox_id=data["sandbox_id"],
|
|
249
262
|
http=self._http,
|
|
250
|
-
vnc_url=
|
|
263
|
+
vnc_url=_absolute_url(self._base_url, data.get("vnc_url", "")),
|
|
251
264
|
host=data.get("host", ""),
|
|
252
265
|
version=data.get("version", ""),
|
|
253
266
|
)
|
|
@@ -255,15 +268,15 @@ class Computer:
|
|
|
255
268
|
return UbuntuSandbox(
|
|
256
269
|
sandbox_id=data["sandbox_id"],
|
|
257
270
|
http=self._http,
|
|
258
|
-
vnc_url=
|
|
271
|
+
vnc_url=_absolute_url(self._base_url, data.get("vnc_url", "")),
|
|
259
272
|
host=data.get("host", ""),
|
|
260
273
|
version=data.get("version", ""),
|
|
261
274
|
)
|
|
262
275
|
return MacOSSandbox(
|
|
263
276
|
sandbox_id=data["sandbox_id"],
|
|
264
277
|
http=self._http,
|
|
265
|
-
vnc_url=
|
|
266
|
-
ssh_url=
|
|
278
|
+
vnc_url=_absolute_url(self._base_url, data.get("vnc_url", "")),
|
|
279
|
+
ssh_url=_absolute_url(self._base_url, data.get("ssh_url", "")),
|
|
267
280
|
)
|
|
268
281
|
|
|
269
282
|
def adhoc_run(
|
|
@@ -281,7 +294,7 @@ class Computer:
|
|
|
281
294
|
|
|
282
295
|
Args:
|
|
283
296
|
instruction: What the agent should do.
|
|
284
|
-
platform: "macos" or "
|
|
297
|
+
platform: "macos", "ios", "windows", or "ubuntu".
|
|
285
298
|
model: Model to use.
|
|
286
299
|
max_steps: Max agent steps.
|
|
287
300
|
files: Optional list of {remote_path, content_b64} to pre-upload.
|
|
@@ -381,6 +394,7 @@ class AsyncComputer:
|
|
|
381
394
|
type: Literal["windows", SandboxType.WINDOWS],
|
|
382
395
|
version: str = ...,
|
|
383
396
|
snapshot: str = ...,
|
|
397
|
+
resources: dict[str, int] | None = ...,
|
|
384
398
|
host: str = ...,
|
|
385
399
|
) -> AsyncWindowsSandbox: ...
|
|
386
400
|
|
|
@@ -391,6 +405,7 @@ class AsyncComputer:
|
|
|
391
405
|
type: Literal["ubuntu", SandboxType.UBUNTU],
|
|
392
406
|
version: str = ...,
|
|
393
407
|
snapshot: str = ...,
|
|
408
|
+
resources: dict[str, int] | None = ...,
|
|
394
409
|
host: str = ...,
|
|
395
410
|
) -> AsyncUbuntuSandbox: ...
|
|
396
411
|
|
|
@@ -404,6 +419,7 @@ class AsyncComputer:
|
|
|
404
419
|
runtime: str = "",
|
|
405
420
|
version: str = "",
|
|
406
421
|
snapshot: str = "",
|
|
422
|
+
resources: dict[str, int] | None = None,
|
|
407
423
|
) -> AsyncMacOSSandbox | AsyncIOSSandbox | AsyncWindowsSandbox | AsyncUbuntuSandbox:
|
|
408
424
|
"""Create a new sandbox.
|
|
409
425
|
|
|
@@ -416,6 +432,8 @@ class AsyncComputer:
|
|
|
416
432
|
version: KVM desktop image version, for example "windows-11",
|
|
417
433
|
"windows-10", or "ubuntu-24.04".
|
|
418
434
|
snapshot: Windur snapshot version returned by sandbox.snapshot().
|
|
435
|
+
resources: KVM CPU/RAM/disk request used to select the baked image
|
|
436
|
+
profile, for example {"cpus": 4, "memory_mb": 4096, "disk_gb": 40}.
|
|
419
437
|
"""
|
|
420
438
|
sandbox_type = _normalize_sandbox_type(type)
|
|
421
439
|
body: dict = {"type": sandbox_type.value}
|
|
@@ -428,6 +446,8 @@ class AsyncComputer:
|
|
|
428
446
|
body["snapshot"] = snapshot
|
|
429
447
|
elif version:
|
|
430
448
|
body["version"] = version
|
|
449
|
+
if resources:
|
|
450
|
+
body["resources"] = resources
|
|
431
451
|
if sandbox_type == SandboxType.IOS:
|
|
432
452
|
if family and (not device_type or not runtime):
|
|
433
453
|
choice = select_simulator(await self.platforms(), family)
|
|
@@ -449,7 +469,7 @@ class AsyncComputer:
|
|
|
449
469
|
return AsyncWindowsSandbox(
|
|
450
470
|
sandbox_id=sid,
|
|
451
471
|
http=self._http,
|
|
452
|
-
vnc_url=
|
|
472
|
+
vnc_url=_absolute_url(self._base_url, data.get("vnc_url", "")),
|
|
453
473
|
host=data.get("host", ""),
|
|
454
474
|
version=data.get("version", ""),
|
|
455
475
|
)
|
|
@@ -457,15 +477,15 @@ class AsyncComputer:
|
|
|
457
477
|
return AsyncUbuntuSandbox(
|
|
458
478
|
sandbox_id=sid,
|
|
459
479
|
http=self._http,
|
|
460
|
-
vnc_url=
|
|
480
|
+
vnc_url=_absolute_url(self._base_url, data.get("vnc_url", "")),
|
|
461
481
|
host=data.get("host", ""),
|
|
462
482
|
version=data.get("version", ""),
|
|
463
483
|
)
|
|
464
484
|
return AsyncMacOSSandbox(
|
|
465
485
|
sandbox_id=sid,
|
|
466
486
|
http=self._http,
|
|
467
|
-
vnc_url=
|
|
468
|
-
ssh_url=
|
|
487
|
+
vnc_url=_absolute_url(self._base_url, data.get("vnc_url", "")),
|
|
488
|
+
ssh_url=_absolute_url(self._base_url, data.get("ssh_url", "")),
|
|
469
489
|
vm_ip=data.get("vm_ip", ""),
|
|
470
490
|
host=data.get("host", ""),
|
|
471
491
|
)
|
|
@@ -482,7 +502,7 @@ class AsyncComputer:
|
|
|
482
502
|
return AsyncWindowsSandbox(
|
|
483
503
|
sandbox_id=data["sandbox_id"],
|
|
484
504
|
http=self._http,
|
|
485
|
-
vnc_url=
|
|
505
|
+
vnc_url=_absolute_url(self._base_url, data.get("vnc_url", "")),
|
|
486
506
|
host=data.get("host", ""),
|
|
487
507
|
version=data.get("version", ""),
|
|
488
508
|
)
|
|
@@ -490,15 +510,15 @@ class AsyncComputer:
|
|
|
490
510
|
return AsyncUbuntuSandbox(
|
|
491
511
|
sandbox_id=data["sandbox_id"],
|
|
492
512
|
http=self._http,
|
|
493
|
-
vnc_url=
|
|
513
|
+
vnc_url=_absolute_url(self._base_url, data.get("vnc_url", "")),
|
|
494
514
|
host=data.get("host", ""),
|
|
495
515
|
version=data.get("version", ""),
|
|
496
516
|
)
|
|
497
517
|
return AsyncMacOSSandbox(
|
|
498
518
|
sandbox_id=data["sandbox_id"],
|
|
499
519
|
http=self._http,
|
|
500
|
-
vnc_url=
|
|
501
|
-
ssh_url=
|
|
520
|
+
vnc_url=_absolute_url(self._base_url, data.get("vnc_url", "")),
|
|
521
|
+
ssh_url=_absolute_url(self._base_url, data.get("ssh_url", "")),
|
|
502
522
|
)
|
|
503
523
|
|
|
504
524
|
async def adhoc_run(
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Shared sandbox resource clients."""
|
|
2
|
+
|
|
3
|
+
from use_computer.resources.accessibility import Accessibility, AsyncAccessibility
|
|
4
|
+
from use_computer.resources.display import AsyncDisplay, Display
|
|
5
|
+
from use_computer.resources.recording import AsyncRecording, Recording
|
|
6
|
+
from use_computer.resources.screenshot import AsyncScreenshot, Screenshot
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"Accessibility",
|
|
10
|
+
"AsyncAccessibility",
|
|
11
|
+
"AsyncDisplay",
|
|
12
|
+
"AsyncRecording",
|
|
13
|
+
"AsyncScreenshot",
|
|
14
|
+
"Display",
|
|
15
|
+
"Recording",
|
|
16
|
+
"Screenshot",
|
|
17
|
+
]
|
|
@@ -11,8 +11,6 @@ from pathlib import Path
|
|
|
11
11
|
|
|
12
12
|
import httpx
|
|
13
13
|
|
|
14
|
-
from use_computer.accessibility import Accessibility, AsyncAccessibility
|
|
15
|
-
from use_computer.display import AsyncDisplay, Display
|
|
16
14
|
from use_computer.errors import PlatformNotSupportedError
|
|
17
15
|
from use_computer.ios.apps import Apps, AsyncApps
|
|
18
16
|
from use_computer.ios.environment import AsyncEnvironment, Environment
|
|
@@ -20,8 +18,16 @@ from use_computer.ios.input import AsyncInput, Input
|
|
|
20
18
|
from use_computer.macos.keyboard import AsyncKeyboard, Keyboard
|
|
21
19
|
from use_computer.macos.mouse import AsyncMouse, Mouse
|
|
22
20
|
from use_computer.models import ActResult, ExecResult
|
|
23
|
-
from use_computer.
|
|
24
|
-
|
|
21
|
+
from use_computer.resources import (
|
|
22
|
+
Accessibility,
|
|
23
|
+
AsyncAccessibility,
|
|
24
|
+
AsyncDisplay,
|
|
25
|
+
AsyncRecording,
|
|
26
|
+
AsyncScreenshot,
|
|
27
|
+
Display,
|
|
28
|
+
Recording,
|
|
29
|
+
Screenshot,
|
|
30
|
+
)
|
|
25
31
|
from use_computer.windows.shell import AsyncShell, Shell, ShellKind
|
|
26
32
|
|
|
27
33
|
|
|
@@ -87,8 +87,13 @@ class TasksClient:
|
|
|
87
87
|
def __init__(self, http: httpx.Client):
|
|
88
88
|
self._http = http
|
|
89
89
|
|
|
90
|
-
def list(
|
|
91
|
-
|
|
90
|
+
def list(
|
|
91
|
+
self, limit: int = 50, offset: int = 0, platform: str | None = None
|
|
92
|
+
) -> builtins.list[TaskSummary]:
|
|
93
|
+
params: dict[str, int | str] = {"limit": limit, "offset": offset}
|
|
94
|
+
if platform:
|
|
95
|
+
params["platform"] = platform
|
|
96
|
+
resp = self._http.get("/admin/tasks", params=params)
|
|
92
97
|
resp.raise_for_status()
|
|
93
98
|
return [
|
|
94
99
|
TaskSummary(
|
|
@@ -149,9 +154,13 @@ class TasksClient:
|
|
|
149
154
|
|
|
150
155
|
|
|
151
156
|
def _build_test_sh(task: Task) -> str:
|
|
152
|
-
"""
|
|
157
|
+
"""Build verifier script for the task's platform."""
|
|
153
158
|
if task.platform == "ios":
|
|
154
159
|
return _build_test_sh_ios(task)
|
|
160
|
+
if task.platform == "windows":
|
|
161
|
+
return _build_test_sh_windows(task)
|
|
162
|
+
if task.platform == "ubuntu":
|
|
163
|
+
return _build_test_sh_ubuntu(task)
|
|
155
164
|
return _build_test_sh_macos(task)
|
|
156
165
|
|
|
157
166
|
|
|
@@ -175,6 +184,24 @@ def _build_test_sh_macos(task: Task) -> str:
|
|
|
175
184
|
return out
|
|
176
185
|
|
|
177
186
|
|
|
187
|
+
def _build_test_sh_windows(task: Task) -> str:
|
|
188
|
+
grader = (task.grader or "").strip()
|
|
189
|
+
if not grader:
|
|
190
|
+
return _tpl("test_windows_nograder.sh")
|
|
191
|
+
check_tpl = _tpl("test_windows_check.sh")
|
|
192
|
+
checks = _render(check_tpl, N="1", CMD=grader.replace("'", "'\\''"))
|
|
193
|
+
return _render(_tpl("test_windows.sh"), CHECKS=checks)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _build_test_sh_ubuntu(task: Task) -> str:
|
|
197
|
+
grader = (task.grader or "").strip()
|
|
198
|
+
if not grader:
|
|
199
|
+
return _tpl("test_ubuntu_nograder.sh")
|
|
200
|
+
check_tpl = _tpl("test_ubuntu_check.sh")
|
|
201
|
+
checks = _render(check_tpl, N="1", CMD=grader.replace("'", "'\\''"))
|
|
202
|
+
return _render(_tpl("test_ubuntu.sh"), CHECKS=checks)
|
|
203
|
+
|
|
204
|
+
|
|
178
205
|
def _build_pre_command_sh(task: Task) -> str | None:
|
|
179
206
|
# `defaults delete` fails on fresh VMs (key never set). Tasks recording such
|
|
180
207
|
# commands likely rely on the cleanup; skip the whole task instead of
|
|
@@ -189,6 +216,19 @@ def _build_pre_command_sh(task: Task) -> str | None:
|
|
|
189
216
|
return _render(_tpl("pre_command.sh"), COMMANDS="\n".join(task.setup_commands))
|
|
190
217
|
|
|
191
218
|
|
|
219
|
+
def _build_pre_command(task: Task) -> tuple[str, str] | None:
|
|
220
|
+
if task.platform == "windows":
|
|
221
|
+
if not task.setup_commands:
|
|
222
|
+
return None
|
|
223
|
+
return "pre_command.ps1", _render(
|
|
224
|
+
_tpl("pre_command.ps1"), COMMANDS="\n".join(task.setup_commands)
|
|
225
|
+
)
|
|
226
|
+
pre = _build_pre_command_sh(task)
|
|
227
|
+
if not pre:
|
|
228
|
+
return None
|
|
229
|
+
return "pre_command.sh", pre
|
|
230
|
+
|
|
231
|
+
|
|
192
232
|
def task_to_harbor(
|
|
193
233
|
task: Task,
|
|
194
234
|
output_root: Path,
|
|
@@ -206,7 +246,7 @@ def task_to_harbor(
|
|
|
206
246
|
tests/
|
|
207
247
|
test.sh
|
|
208
248
|
setup/
|
|
209
|
-
pre_command.sh
|
|
249
|
+
pre_command.sh | pre_command.ps1
|
|
210
250
|
files/
|
|
211
251
|
manifest.json
|
|
212
252
|
<local_name>...
|
|
@@ -225,7 +265,7 @@ def task_to_harbor(
|
|
|
225
265
|
|
|
226
266
|
# Build pre_command early so we fail before creating any dirs if all setup
|
|
227
267
|
# commands got stripped (raises ValueError).
|
|
228
|
-
pre_cmd =
|
|
268
|
+
pre_cmd = _build_pre_command(task)
|
|
229
269
|
|
|
230
270
|
if task_dir.exists():
|
|
231
271
|
shutil.rmtree(task_dir)
|
|
@@ -277,13 +317,15 @@ def task_to_harbor(
|
|
|
277
317
|
json.dumps({"steps": actions}, indent=2) + "\n", encoding="utf-8"
|
|
278
318
|
)
|
|
279
319
|
|
|
280
|
-
# tests/setup/pre_command
|
|
320
|
+
# tests/setup/pre_command.* — skip directory entirely if no setup commands
|
|
281
321
|
setup_dir = tests_dir / "setup"
|
|
282
322
|
if pre_cmd:
|
|
323
|
+
pre_cmd_name, pre_cmd_text = pre_cmd
|
|
283
324
|
setup_dir.mkdir(exist_ok=True)
|
|
284
|
-
pre_cmd_path = setup_dir /
|
|
285
|
-
pre_cmd_path.write_text(
|
|
286
|
-
|
|
325
|
+
pre_cmd_path = setup_dir / pre_cmd_name
|
|
326
|
+
pre_cmd_path.write_text(pre_cmd_text, encoding="utf-8")
|
|
327
|
+
if pre_cmd_name.endswith(".sh"):
|
|
328
|
+
pre_cmd_path.chmod(0o755)
|
|
287
329
|
|
|
288
330
|
# tests/setup/files/<local_name> + manifest.json — files captured at
|
|
289
331
|
# collect time. The runner's setup.py reads manifest.json and uploads
|
|
@@ -315,7 +357,10 @@ def task_to_harbor(
|
|
|
315
357
|
logger.warning("collected file missing on gateway: %s", local_name)
|
|
316
358
|
continue
|
|
317
359
|
(files_dir / local_name).write_bytes(data)
|
|
318
|
-
|
|
360
|
+
entry = {"remote_path": remote_path, "local_name": local_name}
|
|
361
|
+
if task.platform in ("windows", "ubuntu"):
|
|
362
|
+
entry["vm_path"] = remote_path
|
|
363
|
+
manifest.append(entry)
|
|
319
364
|
logger.info("exported file %s → %s (%dB)", local_name, remote_path, len(data))
|
|
320
365
|
if manifest:
|
|
321
366
|
(files_dir / "manifest.json").write_text(
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{COMMANDS}}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Ubuntu test.sh runs on the runner host. Use the gateway SDK/API to inspect
|
|
3
|
+
# the live sandbox; bash state setup runs separately in pre_command.sh.
|
|
4
|
+
REWARD="${HARBOR_REWARD_FILE:-/logs/verifier/reward.txt}"
|
|
5
|
+
GRADER_LOG="${HARBOR_GRADER_LOG:-/logs/verifier/grader.log}"
|
|
6
|
+
: "${GATEWAY_URL:?GATEWAY_URL is required}"
|
|
7
|
+
: "${SANDBOX_ID:?SANDBOX_ID is required}"
|
|
8
|
+
: "${USE_COMPUTER_API_KEY:?USE_COMPUTER_API_KEY is required}"
|
|
9
|
+
export MMINI_API_KEY="${MMINI_API_KEY:-$USE_COMPUTER_API_KEY}"
|
|
10
|
+
mkdir -p "$(dirname "$GRADER_LOG")" 2>/dev/null || true
|
|
11
|
+
|
|
12
|
+
write_reward() {
|
|
13
|
+
mkdir -p "$(dirname "$REWARD")" 2>/dev/null || true
|
|
14
|
+
echo "$1" > "$REWARD" 2>/dev/null || true
|
|
15
|
+
echo "REWARD=$1"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
{{CHECKS}}
|
|
19
|
+
write_reward 0
|
|
20
|
+
echo "Score: 0"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Windows test.sh runs on the runner host. Use the gateway SDK/API to inspect
|
|
3
|
+
# the live sandbox; PowerShell state setup runs separately in pre_command.ps1.
|
|
4
|
+
REWARD="${HARBOR_REWARD_FILE:-/logs/verifier/reward.txt}"
|
|
5
|
+
GRADER_LOG="${HARBOR_GRADER_LOG:-/logs/verifier/grader.log}"
|
|
6
|
+
: "${GATEWAY_URL:?GATEWAY_URL is required}"
|
|
7
|
+
: "${SANDBOX_ID:?SANDBOX_ID is required}"
|
|
8
|
+
: "${USE_COMPUTER_API_KEY:?USE_COMPUTER_API_KEY is required}"
|
|
9
|
+
export MMINI_API_KEY="${MMINI_API_KEY:-$USE_COMPUTER_API_KEY}"
|
|
10
|
+
mkdir -p "$(dirname "$GRADER_LOG")" 2>/dev/null || true
|
|
11
|
+
|
|
12
|
+
write_reward() {
|
|
13
|
+
mkdir -p "$(dirname "$REWARD")" 2>/dev/null || true
|
|
14
|
+
echo "$1" > "$REWARD" 2>/dev/null || true
|
|
15
|
+
echo "REWARD=$1"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
{{CHECKS}}
|
|
19
|
+
write_reward 0
|
|
20
|
+
echo "Score: 0"
|
|
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
|
|
File without changes
|
|
File without changes
|
{use_computer-0.0.26/use_computer → use_computer-0.0.27/use_computer/resources}/accessibility.py
RENAMED
|
File without changes
|
|
File without changes
|
{use_computer-0.0.26/use_computer → use_computer-0.0.27/use_computer/resources}/recording.py
RENAMED
|
File without changes
|
{use_computer-0.0.26/use_computer → use_computer-0.0.27/use_computer/resources}/screenshot.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{use_computer-0.0.26 → use_computer-0.0.27}/use_computer/tasks/templates/test_ios_nograder.sh
RENAMED
|
File without changes
|
|
File without changes
|
{use_computer-0.0.26 → use_computer-0.0.27}/use_computer/tasks/templates/test_macos_check.sh
RENAMED
|
File without changes
|
{use_computer-0.0.26 → use_computer-0.0.27}/use_computer/tasks/templates/test_macos_nograder.sh
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|