use-computer 0.0.7__tar.gz → 0.0.9__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 (44) hide show
  1. {use_computer-0.0.7 → use_computer-0.0.9}/PKG-INFO +6 -1
  2. {use_computer-0.0.7 → use_computer-0.0.9}/README.md +4 -0
  3. {use_computer-0.0.7 → use_computer-0.0.9}/pyproject.toml +4 -4
  4. {use_computer-0.0.7/mmini → use_computer-0.0.9/use_computer}/__init__.py +12 -9
  5. {use_computer-0.0.7/mmini → use_computer-0.0.9/use_computer}/ax_transpile.py +1 -1
  6. {use_computer-0.0.7/mmini → use_computer-0.0.9/use_computer}/client.py +8 -8
  7. {use_computer-0.0.7/mmini → use_computer-0.0.9/use_computer}/display.py +1 -1
  8. {use_computer-0.0.7/mmini → use_computer-0.0.9/use_computer}/errors.py +4 -4
  9. use_computer-0.0.9/use_computer/ios/__init__.py +14 -0
  10. {use_computer-0.0.7/mmini → use_computer-0.0.9/use_computer}/ios/apps.py +1 -1
  11. {use_computer-0.0.7/mmini → use_computer-0.0.9/use_computer}/ios/environment.py +1 -1
  12. {use_computer-0.0.7/mmini → use_computer-0.0.9/use_computer}/ios/input.py +1 -1
  13. use_computer-0.0.9/use_computer/macos/__init__.py +4 -0
  14. {use_computer-0.0.7/mmini → use_computer-0.0.9/use_computer}/macos/keyboard.py +1 -1
  15. {use_computer-0.0.7/mmini → use_computer-0.0.9/use_computer}/macos/mouse.py +1 -1
  16. {use_computer-0.0.7/mmini → use_computer-0.0.9/use_computer}/parsers.py +3 -3
  17. {use_computer-0.0.7/mmini → use_computer-0.0.9/use_computer}/recording.py +1 -1
  18. {use_computer-0.0.7/mmini → use_computer-0.0.9/use_computer}/retry.py +2 -2
  19. {use_computer-0.0.7/mmini → use_computer-0.0.9/use_computer}/sandbox.py +11 -11
  20. {use_computer-0.0.7/mmini → use_computer-0.0.9/use_computer}/tasks/__init__.py +1 -1
  21. use_computer-0.0.9/use_computer/vision.py +77 -0
  22. {use_computer-0.0.7 → use_computer-0.0.9}/uv.lock +104 -2
  23. use_computer-0.0.7/mmini/ios/__init__.py +0 -14
  24. use_computer-0.0.7/mmini/macos/__init__.py +0 -4
  25. use_computer-0.0.7/use_computer/__init__.py +0 -15
  26. use_computer-0.0.7/use_computer/py.typed +0 -1
  27. {use_computer-0.0.7 → use_computer-0.0.9}/.env.example +0 -0
  28. {use_computer-0.0.7 → use_computer-0.0.9}/.gitignore +0 -0
  29. {use_computer-0.0.7 → use_computer-0.0.9}/.pre-commit-config.yaml +0 -0
  30. {use_computer-0.0.7 → use_computer-0.0.9}/examples/_1_hello_macos.py +0 -0
  31. {use_computer-0.0.7 → use_computer-0.0.9}/examples/_2_hello_ios.py +0 -0
  32. {use_computer-0.0.7 → use_computer-0.0.9}/examples/_3_recording.py +0 -0
  33. {use_computer-0.0.7 → use_computer-0.0.9}/examples/_4_file_transfer.py +0 -0
  34. {use_computer-0.0.7 → use_computer-0.0.9}/examples/_5_keepalive.py +0 -0
  35. {use_computer-0.0.7/mmini → use_computer-0.0.9/use_computer}/models.py +0 -0
  36. {use_computer-0.0.7/mmini → use_computer-0.0.9/use_computer}/py.typed +0 -0
  37. {use_computer-0.0.7/mmini → use_computer-0.0.9/use_computer}/screenshot.py +0 -0
  38. {use_computer-0.0.7/mmini → use_computer-0.0.9/use_computer}/tasks/templates/pre_command.sh +0 -0
  39. {use_computer-0.0.7/mmini → use_computer-0.0.9/use_computer}/tasks/templates/task.toml +0 -0
  40. {use_computer-0.0.7/mmini → use_computer-0.0.9/use_computer}/tasks/templates/test_ios.sh +0 -0
  41. {use_computer-0.0.7/mmini → use_computer-0.0.9/use_computer}/tasks/templates/test_ios_nograder.sh +0 -0
  42. {use_computer-0.0.7/mmini → use_computer-0.0.9/use_computer}/tasks/templates/test_macos.sh +0 -0
  43. {use_computer-0.0.7/mmini → use_computer-0.0.9/use_computer}/tasks/templates/test_macos_check.sh +0 -0
  44. {use_computer-0.0.7/mmini → use_computer-0.0.9/use_computer}/tasks/templates/test_macos_nograder.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: use-computer
3
- Version: 0.0.7
3
+ Version: 0.0.9
4
4
  Summary: Python SDK for the use.computer macOS and iOS Computer Use API
5
5
  Project-URL: Homepage, https://use.computer
6
6
  Project-URL: Documentation, https://api.use.computer/docs
@@ -18,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.13
18
18
  Classifier: Typing :: Typed
19
19
  Requires-Python: >=3.10
20
20
  Requires-Dist: httpx>=0.27
21
+ Requires-Dist: pillow>=10
21
22
  Description-Content-Type: text/markdown
22
23
 
23
24
  # use-computer Python SDK
@@ -52,6 +53,10 @@ Full DSL reference (macOS + iOS): [docs.use.computer/docs/sdk](https://docs.use.
52
53
 
53
54
  For agent loops and evals: [use-computer-cookbook](https://github.com/josancamon19/use-computer-cookbook).
54
55
 
56
+ ## For AI coding assistants
57
+
58
+ The cookbook ships a Claude-Code-ready skill that teaches an assistant how to use this SDK end-to-end (macOS + iOS DSL, recording, file transfer, keepalive, retries, the AppleScript/AX transpiler, the vision-model coord scaling). Point your assistant at [`skills/use-computer-sdk/SKILL.md`](https://github.com/josancamon19/use-computer-cookbook/blob/main/skills/use-computer-sdk/SKILL.md) — the body is short and links out to per-topic references in `skills/use-computer-sdk/references/`.
59
+
55
60
  ## HTTP API
56
61
 
57
62
  Every SDK method wraps `https://api.use.computer/v1/...` with `Authorization: Bearer mk_live_...`. Swagger: [api.use.computer/docs](https://api.use.computer/docs). OpenAPI spec: [api.use.computer/openapi.yaml](https://api.use.computer/openapi.yaml).
@@ -30,6 +30,10 @@ Full DSL reference (macOS + iOS): [docs.use.computer/docs/sdk](https://docs.use.
30
30
 
31
31
  For agent loops and evals: [use-computer-cookbook](https://github.com/josancamon19/use-computer-cookbook).
32
32
 
33
+ ## For AI coding assistants
34
+
35
+ The cookbook ships a Claude-Code-ready skill that teaches an assistant how to use this SDK end-to-end (macOS + iOS DSL, recording, file transfer, keepalive, retries, the AppleScript/AX transpiler, the vision-model coord scaling). Point your assistant at [`skills/use-computer-sdk/SKILL.md`](https://github.com/josancamon19/use-computer-cookbook/blob/main/skills/use-computer-sdk/SKILL.md) — the body is short and links out to per-topic references in `skills/use-computer-sdk/references/`.
36
+
33
37
  ## HTTP API
34
38
 
35
39
  Every SDK method wraps `https://api.use.computer/v1/...` with `Authorization: Bearer mk_live_...`. Swagger: [api.use.computer/docs](https://api.use.computer/docs). OpenAPI spec: [api.use.computer/openapi.yaml](https://api.use.computer/openapi.yaml).
@@ -4,12 +4,12 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "use-computer"
7
- version = "0.0.7"
7
+ version = "0.0.9"
8
8
  description = "Python SDK for the use.computer macOS and iOS Computer Use API"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
11
11
  authors = [{ name = "use.computer" }]
12
- dependencies = ["httpx>=0.27"]
12
+ dependencies = ["httpx>=0.27", "Pillow>=10"]
13
13
  keywords = ["computer-use", "macos", "ios", "automation", "sandbox"]
14
14
  classifiers = [
15
15
  "Development Status :: 3 - Alpha",
@@ -38,7 +38,7 @@ Documentation = "https://api.use.computer/docs"
38
38
  Repository = "https://github.com/josancamon19/use-computer-python"
39
39
 
40
40
  [tool.hatch.build.targets.wheel]
41
- packages = ["mmini", "use_computer"]
41
+ packages = ["use_computer"]
42
42
 
43
43
  [tool.hatch.build]
44
44
  exclude = [
@@ -48,7 +48,7 @@ exclude = [
48
48
  "/.venv",
49
49
  "/dist",
50
50
  "/results",
51
- "/mmini/test_*.py",
51
+ "/use_computer/test_*.py",
52
52
  ]
53
53
 
54
54
  [tool.ruff]
@@ -1,6 +1,6 @@
1
- from mmini.client import AsyncMmini, Mmini, RunStatus
2
- from mmini.errors import MminiError, PlatformNotSupportedError
3
- from mmini.models import (
1
+ from use_computer.client import AsyncComputer, Computer, RunStatus
2
+ from use_computer.errors import PlatformNotSupportedError, UseComputerError
3
+ from use_computer.models import (
4
4
  ActionResult,
5
5
  ActResult,
6
6
  CursorPosition,
@@ -8,8 +8,8 @@ from mmini.models import (
8
8
  ExecResult,
9
9
  RecordingInfo,
10
10
  )
11
- from mmini.parsers import Action, parse_pyautogui, parse_xdotool
12
- from mmini.sandbox import (
11
+ from use_computer.parsers import Action, parse_pyautogui, parse_xdotool
12
+ from use_computer.sandbox import (
13
13
  AsyncIOSSandbox,
14
14
  AsyncMacOSSandbox,
15
15
  AsyncSandbox,
@@ -18,7 +18,8 @@ from mmini.sandbox import (
18
18
  Sandbox,
19
19
  SandboxType,
20
20
  )
21
- from mmini.tasks import Task, TasksClient, TaskSummary
21
+ from use_computer.tasks import Task, TasksClient, TaskSummary
22
+ from use_computer.vision import scale_screenshot_for_model, screenshot_cap_for_model
22
23
 
23
24
  __all__ = [
24
25
  "Action",
@@ -26,15 +27,15 @@ __all__ = [
26
27
  "ActResult",
27
28
  "AsyncIOSSandbox",
28
29
  "AsyncMacOSSandbox",
29
- "AsyncMmini",
30
+ "AsyncComputer",
30
31
  "AsyncSandbox",
31
32
  "CursorPosition",
32
33
  "DisplayInfo",
33
34
  "ExecResult",
34
35
  "IOSSandbox",
35
36
  "MacOSSandbox",
36
- "Mmini",
37
- "MminiError",
37
+ "Computer",
38
+ "UseComputerError",
38
39
  "PlatformNotSupportedError",
39
40
  "RecordingInfo",
40
41
  "RunStatus",
@@ -45,4 +46,6 @@ __all__ = [
45
46
  "TasksClient",
46
47
  "parse_pyautogui",
47
48
  "parse_xdotool",
49
+ "scale_screenshot_for_model",
50
+ "screenshot_cap_for_model",
48
51
  ]
@@ -464,7 +464,7 @@ _B64_PAYLOAD_RE = re.compile(r"echo ([A-Za-z0-9+/=]+) \| base64 -d \| bash")
464
464
 
465
465
  def patch_curl_timeouts(text: str) -> tuple[str, int]:
466
466
  """Add -m 5 to `curl -s -X POST` calls that are buried inside base64-encoded
467
- payloads (the ax_helper emission style). Used by mmini._run_setup to harden
467
+ payloads (the ax_helper emission style). Used by use_computer._run_setup to harden
468
468
  already-baked test.sh files whose payloads were generated before the -m flag
469
469
  was added to _emit_helper_call.
470
470
 
@@ -8,9 +8,9 @@ from typing import Any, Literal, overload
8
8
 
9
9
  import httpx
10
10
 
11
- from mmini.errors import _araise_if_unsupported, _raise_if_unsupported
12
- from mmini.retry import AsyncRetryTransport, RetryTransport
13
- from mmini.sandbox import (
11
+ from use_computer.errors import _araise_if_unsupported, _raise_if_unsupported
12
+ from use_computer.retry import AsyncRetryTransport, RetryTransport
13
+ from use_computer.sandbox import (
14
14
  AsyncIOSSandbox,
15
15
  AsyncMacOSSandbox,
16
16
  AsyncSandbox,
@@ -19,7 +19,7 @@ from mmini.sandbox import (
19
19
  Sandbox,
20
20
  SandboxType,
21
21
  )
22
- from mmini.tasks import TasksClient
22
+ from use_computer.tasks import TasksClient
23
23
 
24
24
  DEFAULT_BASE_URL = "https://api.use.computer"
25
25
 
@@ -77,8 +77,8 @@ class RunStatus:
77
77
  )
78
78
 
79
79
 
80
- class Mmini:
81
- """mmini client. Creates and manages macOS and iOS sandboxes."""
80
+ class Computer:
81
+ """use.computer client. Creates and manages macOS and iOS sandboxes."""
82
82
 
83
83
  def __init__(self, api_key: str | None = None, base_url: str | None = None):
84
84
  self._base_url = _resolve_base_url(base_url)
@@ -224,8 +224,8 @@ class Mmini:
224
224
  self.close()
225
225
 
226
226
 
227
- class AsyncMmini:
228
- """Async mmini client."""
227
+ class AsyncComputer:
228
+ """Async use.computer client."""
229
229
 
230
230
  def __init__(self, api_key: str | None = None, base_url: str | None = None):
231
231
  self._base_url = _resolve_base_url(base_url)
@@ -4,7 +4,7 @@ from typing import Any
4
4
 
5
5
  import httpx
6
6
 
7
- from mmini.models import DisplayInfo
7
+ from use_computer.models import DisplayInfo
8
8
 
9
9
 
10
10
  class Display:
@@ -1,4 +1,4 @@
1
- """Typed exceptions for mmini SDK errors that callers should catch by class
1
+ """Typed exceptions for use.computer SDK errors that callers should catch by class
2
2
  rather than by parsing HTTP status codes or response bodies."""
3
3
 
4
4
  from __future__ import annotations
@@ -6,11 +6,11 @@ from __future__ import annotations
6
6
  import httpx
7
7
 
8
8
 
9
- class MminiError(Exception):
10
- """Base class for typed mmini SDK errors."""
9
+ class UseComputerError(Exception):
10
+ """Base class for typed use.computer SDK errors."""
11
11
 
12
12
 
13
- class PlatformNotSupportedError(MminiError):
13
+ class PlatformNotSupportedError(UseComputerError):
14
14
  """Raised when an action is not supported on the sandbox's platform.
15
15
 
16
16
  The gateway returns 501 with a JSON body of the shape:
@@ -0,0 +1,14 @@
1
+ from use_computer.ios.apps import Apps, AsyncApps
2
+ from use_computer.ios.environment import AsyncEnvironment, Environment
3
+ from use_computer.ios.input import AsyncInput, Button, Input, Key
4
+
5
+ __all__ = [
6
+ "Apps",
7
+ "AsyncApps",
8
+ "AsyncEnvironment",
9
+ "AsyncInput",
10
+ "Button",
11
+ "Environment",
12
+ "Input",
13
+ "Key",
14
+ ]
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import httpx
4
4
 
5
- from mmini.models import ActionResult
5
+ from use_computer.models import ActionResult
6
6
 
7
7
 
8
8
  class Apps:
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import httpx
4
4
 
5
- from mmini.models import ActionResult
5
+ from use_computer.models import ActionResult
6
6
 
7
7
 
8
8
  class Environment:
@@ -4,7 +4,7 @@ from enum import Enum
4
4
 
5
5
  import httpx
6
6
 
7
- from mmini.models import ActionResult
7
+ from use_computer.models import ActionResult
8
8
 
9
9
 
10
10
  class Button(str, Enum):
@@ -0,0 +1,4 @@
1
+ from use_computer.macos.keyboard import AsyncKeyboard, Keyboard
2
+ from use_computer.macos.mouse import AsyncMouse, Mouse
3
+
4
+ __all__ = ["AsyncKeyboard", "AsyncMouse", "Keyboard", "Mouse"]
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import httpx
4
4
 
5
- from mmini.models import ActionResult
5
+ from use_computer.models import ActionResult
6
6
 
7
7
 
8
8
  class Keyboard:
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import httpx
4
4
 
5
- from mmini.models import ActionResult, CursorPosition
5
+ from use_computer.models import ActionResult, CursorPosition
6
6
 
7
7
 
8
8
  class Mouse:
@@ -1,10 +1,10 @@
1
- """Parse pyautogui and xdotool commands into mmini Sandbox calls.
1
+ """Parse pyautogui and xdotool commands into use.computer Sandbox calls.
2
2
 
3
3
  Models like Claude and GPT sometimes output raw pyautogui or xdotool commands.
4
4
  These parsers translate them into Sandbox method calls.
5
5
 
6
6
  Usage:
7
- from mmini.parsers import parse_pyautogui, parse_xdotool
7
+ from use_computer.parsers import parse_pyautogui, parse_xdotool
8
8
 
9
9
  for action in parse_pyautogui("pyautogui.click(100, 200)"):
10
10
  action.execute(sandbox)
@@ -20,7 +20,7 @@ from dataclasses import dataclass, field
20
20
  from typing import TYPE_CHECKING, Any
21
21
 
22
22
  if TYPE_CHECKING:
23
- from mmini.sandbox import Sandbox
23
+ from use_computer.sandbox import Sandbox
24
24
 
25
25
 
26
26
  @dataclass
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import httpx
4
4
 
5
- from mmini.models import RecordingInfo
5
+ from use_computer.models import RecordingInfo
6
6
 
7
7
 
8
8
  class Recording:
@@ -1,4 +1,4 @@
1
- """Single retry layer for all mmini SDK HTTP calls.
1
+ """Single retry layer for all use.computer SDK HTTP calls.
2
2
 
3
3
  Retries are handled at the httpx transport level so they apply
4
4
  automatically to every request — no per-method wrappers needed.
@@ -12,7 +12,7 @@ import time
12
12
 
13
13
  import httpx
14
14
 
15
- _log = logging.getLogger("mmini.retry")
15
+ _log = logging.getLogger("use_computer.retry")
16
16
 
17
17
  RETRYABLE_EXCEPTIONS = (
18
18
  httpx.TimeoutException,
@@ -9,15 +9,15 @@ from pathlib import Path
9
9
 
10
10
  import httpx
11
11
 
12
- from mmini.display import AsyncDisplay, Display
13
- from mmini.ios.apps import Apps, AsyncApps
14
- from mmini.ios.environment import AsyncEnvironment, Environment
15
- from mmini.ios.input import AsyncInput, Input
16
- from mmini.macos.keyboard import AsyncKeyboard, Keyboard
17
- from mmini.macos.mouse import AsyncMouse, Mouse
18
- from mmini.models import ActResult, ExecResult
19
- from mmini.recording import AsyncRecording, Recording
20
- from mmini.screenshot import AsyncScreenshot, Screenshot
12
+ from use_computer.display import AsyncDisplay, Display
13
+ from use_computer.ios.apps import Apps, AsyncApps
14
+ from use_computer.ios.environment import AsyncEnvironment, Environment
15
+ from use_computer.ios.input import AsyncInput, Input
16
+ from use_computer.macos.keyboard import AsyncKeyboard, Keyboard
17
+ from use_computer.macos.mouse import AsyncMouse, Mouse
18
+ from use_computer.models import ActResult, ExecResult
19
+ from use_computer.recording import AsyncRecording, Recording
20
+ from use_computer.screenshot import AsyncScreenshot, Screenshot
21
21
 
22
22
 
23
23
  class SandboxType(str, Enum):
@@ -222,7 +222,7 @@ class MacOSSandbox(Sandbox):
222
222
  with tarfile.open(fileobj=buf, mode="w:gz") as tar:
223
223
  for item in local_dir.rglob("*"):
224
224
  tar.add(str(item), arcname=str(item.relative_to(local_dir)))
225
- tmp = f"/tmp/_mmini_upload_{self.sandbox_id[-8:]}.tar.gz"
225
+ tmp = f"/tmp/_use_computer_upload_{self.sandbox_id[-8:]}.tar.gz"
226
226
  self.upload_bytes(buf.getvalue(), tmp)
227
227
  self.exec_ssh(f'mkdir -p "{remote_dir}" && tar xzf {tmp} -C "{remote_dir}" && rm -f {tmp}')
228
228
 
@@ -390,7 +390,7 @@ class AsyncMacOSSandbox(AsyncSandbox):
390
390
  with tarfile.open(fileobj=buf, mode="w:gz") as tar:
391
391
  for item in local_dir.rglob("*"):
392
392
  tar.add(str(item), arcname=str(item.relative_to(local_dir)))
393
- tmp = f"/tmp/_mmini_upload_{self.sandbox_id[-8:]}.tar.gz"
393
+ tmp = f"/tmp/_use_computer_upload_{self.sandbox_id[-8:]}.tar.gz"
394
394
  await self.upload_bytes(buf.getvalue(), tmp)
395
395
  cmd = f'mkdir -p "{remote_dir}" && tar xzf {tmp} -C "{remote_dir}" && rm -f {tmp}'
396
396
  await self.exec_ssh(cmd)
@@ -11,7 +11,7 @@ from pathlib import Path
11
11
 
12
12
  import httpx
13
13
 
14
- from mmini.ax_transpile import patch_curl_timeouts, transpile
14
+ from use_computer.ax_transpile import patch_curl_timeouts, transpile
15
15
 
16
16
  logger = logging.getLogger(__name__)
17
17
 
@@ -0,0 +1,77 @@
1
+ """Client-side screenshot resize + coord scaling for vision models.
2
+
3
+ Background — why this lives in the SDK
4
+ --------------------------------------
5
+ Computer-use models (Anthropic CUA, OpenAI computer-use-preview, Gemini,
6
+ Kimi/Fireworks, etc.) return click coordinates **in resized-image space**,
7
+ not the original screenshot's pixel space. Each family also resizes the
8
+ image to its own internal cap if you send something larger — and that
9
+ resize is silent and only loosely documented. If you don't account for it,
10
+ clicks land at the wrong place and you debug in circles.
11
+
12
+ The cleanest fix is to resize the screenshot to a known target *before*
13
+ sending it to the model, then multiply the returned coords by the inverse
14
+ scale at dispatch time. That's what `scale_screenshot_for_model` does.
15
+ `mac.mouse.click` takes native display points, so the multiplication
16
+ brings model coords back into the right space.
17
+
18
+ Per-model caps are probed empirically and live in
19
+ `screenshot_cap_for_model`. Tweak there, not at call sites.
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import io
25
+
26
+ from PIL import Image
27
+
28
+
29
+ def screenshot_cap_for_model(model: str) -> int:
30
+ """Long-edge cap (in pixels) for the resize that precedes vision input.
31
+
32
+ Per-family caps are probed for click accuracy; lower caps trade detail
33
+ for stable coordinates on models that drift past their internal limit.
34
+ """
35
+ m = (model or "").lower()
36
+ if "opus-4-7" in m or "opus-4-8" in m or "opus-5" in m:
37
+ return 2576 # Anthropic Opus 4.7+ caps at 2576px
38
+ if "claude" in m or "anthropic/" in m:
39
+ return 1568 # Other Anthropic models cap at 1568px
40
+ if "kimi" in m or "fireworks" in m:
41
+ return 896 # Kimi: y-coord accuracy degrades at >1024 (probed on tall iOS shots)
42
+ return 1280 # OpenAI/Gemini fallback
43
+
44
+
45
+ def scale_screenshot_for_model(
46
+ image_bytes: bytes, model: str
47
+ ) -> tuple[bytes, int, int, float, float]:
48
+ """Aspect-preserving resize of a screenshot to the model's vision cap.
49
+
50
+ Returns `(resized_bytes, api_w, api_h, sx, sy)`:
51
+ - `resized_bytes` — encoded image to send to the model
52
+ - `api_w`, `api_h` — the resized image's dimensions
53
+ - `sx`, `sy` — multiply model-returned coords by these to recover
54
+ native display points
55
+
56
+ If the image already fits within the cap, returns the input bytes
57
+ untouched and `sx == sy == 1.0`. JPEG inputs stay JPEG (quality=85);
58
+ PNG inputs stay PNG.
59
+ """
60
+ img = Image.open(io.BytesIO(image_bytes))
61
+ native_w, native_h = img.size
62
+ target = screenshot_cap_for_model(model)
63
+ scale = min(target / native_w, target / native_h, 1.0)
64
+ api_w = int(native_w * scale)
65
+ api_h = int(native_h * scale)
66
+ sx = native_w / api_w
67
+ sy = native_h / api_h
68
+ if scale == 1.0:
69
+ return image_bytes, api_w, api_h, sx, sy
70
+ resized = img.resize((api_w, api_h), Image.Resampling.LANCZOS)
71
+ buf = io.BytesIO()
72
+ fmt = (img.format or "PNG").upper()
73
+ if fmt == "JPEG":
74
+ resized.convert("RGB").save(buf, format="JPEG", quality=85)
75
+ else:
76
+ resized.save(buf, format="PNG")
77
+ return buf.getvalue(), api_w, api_h, sx, sy
@@ -513,6 +513,104 @@ wheels = [
513
513
  { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" },
514
514
  ]
515
515
 
516
+ [[package]]
517
+ name = "pillow"
518
+ version = "12.2.0"
519
+ source = { registry = "https://pypi.org/simple" }
520
+ sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" }
521
+ wheels = [
522
+ { url = "https://files.pythonhosted.org/packages/3a/aa/d0b28e1c811cd4d5f5c2bfe2e022292bd255ae5744a3b9ac7d6c8f72dd75/pillow-12.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:a4e8f36e677d3336f35089648c8955c51c6d386a13cf6ee9c189c5f5bd713a9f", size = 5354355, upload-time = "2026-04-01T14:42:15.402Z" },
523
+ { url = "https://files.pythonhosted.org/packages/27/8e/1d5b39b8ae2bd7650d0c7b6abb9602d16043ead9ebbfef4bc4047454da2a/pillow-12.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e589959f10d9824d39b350472b92f0ce3b443c0a3442ebf41c40cb8361c5b97", size = 4695871, upload-time = "2026-04-01T14:42:18.234Z" },
524
+ { url = "https://files.pythonhosted.org/packages/f0/c5/dcb7a6ca6b7d3be41a76958e90018d56c8462166b3ef223150360850c8da/pillow-12.2.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a52edc8bfff4429aaabdf4d9ee0daadbbf8562364f940937b941f87a4290f5ff", size = 6269734, upload-time = "2026-04-01T14:42:20.608Z" },
525
+ { url = "https://files.pythonhosted.org/packages/ea/f1/aa1bb13b2f4eba914e9637893c73f2af8e48d7d4023b9d3750d4c5eb2d0c/pillow-12.2.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:975385f4776fafde056abb318f612ef6285b10a1f12b8570f3647ad0d74b48ec", size = 8076080, upload-time = "2026-04-01T14:42:23.095Z" },
526
+ { url = "https://files.pythonhosted.org/packages/a1/2a/8c79d6a53169937784604a8ae8d77e45888c41537f7f6f65ed1f407fe66d/pillow-12.2.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd9c0c7a0c681a347b3194c500cb1e6ca9cab053ea4d82a5cf45b6b754560136", size = 6382236, upload-time = "2026-04-01T14:42:25.82Z" },
527
+ { url = "https://files.pythonhosted.org/packages/b5/42/bbcb6051030e1e421d103ce7a8ecadf837aa2f39b8f82ef1a8d37c3d4ebc/pillow-12.2.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:88d387ff40b3ff7c274947ed3125dedf5262ec6919d83946753b5f3d7c67ea4c", size = 7070220, upload-time = "2026-04-01T14:42:28.68Z" },
528
+ { url = "https://files.pythonhosted.org/packages/3f/e1/c2a7d6dd8cfa6b231227da096fd2d58754bab3603b9d73bf609d3c18b64f/pillow-12.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:51c4167c34b0d8ba05b547a3bb23578d0ba17b80a5593f93bd8ecb123dd336a3", size = 6493124, upload-time = "2026-04-01T14:42:31.579Z" },
529
+ { url = "https://files.pythonhosted.org/packages/5f/41/7c8617da5d32e1d2f026e509484fdb6f3ad7efaef1749a0c1928adbb099e/pillow-12.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:34c0d99ecccea270c04882cb3b86e7b57296079c9a4aff88cb3b33563d95afaa", size = 7194324, upload-time = "2026-04-01T14:42:34.615Z" },
530
+ { url = "https://files.pythonhosted.org/packages/2d/de/a777627e19fd6d62f84070ee1521adde5eeda4855b5cf60fe0b149118bca/pillow-12.2.0-cp310-cp310-win32.whl", hash = "sha256:b85f66ae9eb53e860a873b858b789217ba505e5e405a24b85c0464822fe88032", size = 6376363, upload-time = "2026-04-01T14:42:37.19Z" },
531
+ { url = "https://files.pythonhosted.org/packages/e7/34/fc4cb5204896465842767b96d250c08410f01f2f28afc43b257de842eed5/pillow-12.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:673aa32138f3e7531ccdbca7b3901dba9b70940a19ccecc6a37c77d5fdeb05b5", size = 7083523, upload-time = "2026-04-01T14:42:39.62Z" },
532
+ { url = "https://files.pythonhosted.org/packages/2d/a0/32852d36bc7709f14dc3f64f929a275e958ad8c19a6deba9610d458e28b3/pillow-12.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:3e080565d8d7c671db5802eedfb438e5565ffa40115216eabb8cd52d0ecce024", size = 2463318, upload-time = "2026-04-01T14:42:42.063Z" },
533
+ { url = "https://files.pythonhosted.org/packages/68/e1/748f5663efe6edcfc4e74b2b93edfb9b8b99b67f21a854c3ae416500a2d9/pillow-12.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:8be29e59487a79f173507c30ddf57e733a357f67881430449bb32614075a40ab", size = 5354347, upload-time = "2026-04-01T14:42:44.255Z" },
534
+ { url = "https://files.pythonhosted.org/packages/47/a1/d5ff69e747374c33a3b53b9f98cca7889fce1fd03d79cdc4e1bccc6c5a87/pillow-12.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:71cde9a1e1551df7d34a25462fc60325e8a11a82cc2e2f54578e5e9a1e153d65", size = 4695873, upload-time = "2026-04-01T14:42:46.452Z" },
535
+ { url = "https://files.pythonhosted.org/packages/df/21/e3fbdf54408a973c7f7f89a23b2cb97a7ef30c61ab4142af31eee6aebc88/pillow-12.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f490f9368b6fc026f021db16d7ec2fbf7d89e2edb42e8ec09d2c60505f5729c7", size = 6280168, upload-time = "2026-04-01T14:42:49.228Z" },
536
+ { url = "https://files.pythonhosted.org/packages/d3/f1/00b7278c7dd52b17ad4329153748f87b6756ec195ff786c2bdf12518337d/pillow-12.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8bd7903a5f2a4545f6fd5935c90058b89d30045568985a71c79f5fd6edf9b91e", size = 8088188, upload-time = "2026-04-01T14:42:51.735Z" },
537
+ { url = "https://files.pythonhosted.org/packages/ad/cf/220a5994ef1b10e70e85748b75649d77d506499352be135a4989c957b701/pillow-12.2.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3997232e10d2920a68d25191392e3a4487d8183039e1c74c2297f00ed1c50705", size = 6394401, upload-time = "2026-04-01T14:42:54.343Z" },
538
+ { url = "https://files.pythonhosted.org/packages/e9/bd/e51a61b1054f09437acfbc2ff9106c30d1eb76bc1453d428399946781253/pillow-12.2.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e74473c875d78b8e9d5da2a70f7099549f9eb37ded4e2f6a463e60125bccd176", size = 7079655, upload-time = "2026-04-01T14:42:56.954Z" },
539
+ { url = "https://files.pythonhosted.org/packages/6b/3d/45132c57d5fb4b5744567c3817026480ac7fc3ce5d4c47902bc0e7f6f853/pillow-12.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:56a3f9c60a13133a98ecff6197af34d7824de9b7b38c3654861a725c970c197b", size = 6503105, upload-time = "2026-04-01T14:42:59.847Z" },
540
+ { url = "https://files.pythonhosted.org/packages/7d/2e/9df2fc1e82097b1df3dce58dc43286aa01068e918c07574711fcc53e6fb4/pillow-12.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90e6f81de50ad6b534cab6e5aef77ff6e37722b2f5d908686f4a5c9eba17a909", size = 7203402, upload-time = "2026-04-01T14:43:02.664Z" },
541
+ { url = "https://files.pythonhosted.org/packages/bd/2e/2941e42858ebb67e50ae741473de81c2984e6eff7b397017623c676e2e8d/pillow-12.2.0-cp311-cp311-win32.whl", hash = "sha256:8c984051042858021a54926eb597d6ee3012393ce9c181814115df4c60b9a808", size = 6378149, upload-time = "2026-04-01T14:43:05.274Z" },
542
+ { url = "https://files.pythonhosted.org/packages/69/42/836b6f3cd7f3e5fa10a1f1a5420447c17966044c8fbf589cc0452d5502db/pillow-12.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e6b2a0c538fc200b38ff9eb6628228b77908c319a005815f2dde585a0664b60", size = 7082626, upload-time = "2026-04-01T14:43:08.557Z" },
543
+ { url = "https://files.pythonhosted.org/packages/c2/88/549194b5d6f1f494b485e493edc6693c0a16f4ada488e5bd974ed1f42fad/pillow-12.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:9a8a34cc89c67a65ea7437ce257cea81a9dad65b29805f3ecee8c8fe8ff25ffe", size = 2463531, upload-time = "2026-04-01T14:43:10.743Z" },
544
+ { url = "https://files.pythonhosted.org/packages/58/be/7482c8a5ebebbc6470b3eb791812fff7d5e0216c2be3827b30b8bb6603ed/pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5", size = 5308279, upload-time = "2026-04-01T14:43:13.246Z" },
545
+ { url = "https://files.pythonhosted.org/packages/d8/95/0a351b9289c2b5cbde0bacd4a83ebc44023e835490a727b2a3bd60ddc0f4/pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421", size = 4695490, upload-time = "2026-04-01T14:43:15.584Z" },
546
+ { url = "https://files.pythonhosted.org/packages/de/af/4e8e6869cbed569d43c416fad3dc4ecb944cb5d9492defaed89ddd6fe871/pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987", size = 6284462, upload-time = "2026-04-01T14:43:18.268Z" },
547
+ { url = "https://files.pythonhosted.org/packages/e9/9e/c05e19657fd57841e476be1ab46c4d501bffbadbafdc31a6d665f8b737b6/pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76", size = 8094744, upload-time = "2026-04-01T14:43:20.716Z" },
548
+ { url = "https://files.pythonhosted.org/packages/2b/54/1789c455ed10176066b6e7e6da1b01e50e36f94ba584dc68d9eebfe9156d/pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005", size = 6398371, upload-time = "2026-04-01T14:43:23.443Z" },
549
+ { url = "https://files.pythonhosted.org/packages/43/e3/fdc657359e919462369869f1c9f0e973f353f9a9ee295a39b1fea8ee1a77/pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780", size = 7087215, upload-time = "2026-04-01T14:43:26.758Z" },
550
+ { url = "https://files.pythonhosted.org/packages/8b/f8/2f6825e441d5b1959d2ca5adec984210f1ec086435b0ed5f52c19b3b8a6e/pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5", size = 6509783, upload-time = "2026-04-01T14:43:29.56Z" },
551
+ { url = "https://files.pythonhosted.org/packages/67/f9/029a27095ad20f854f9dba026b3ea6428548316e057e6fc3545409e86651/pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5", size = 7212112, upload-time = "2026-04-01T14:43:32.091Z" },
552
+ { url = "https://files.pythonhosted.org/packages/be/42/025cfe05d1be22dbfdb4f264fe9de1ccda83f66e4fc3aac94748e784af04/pillow-12.2.0-cp312-cp312-win32.whl", hash = "sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940", size = 6378489, upload-time = "2026-04-01T14:43:34.601Z" },
553
+ { url = "https://files.pythonhosted.org/packages/5d/7b/25a221d2c761c6a8ae21bfa3874988ff2583e19cf8a27bf2fee358df7942/pillow-12.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5", size = 7084129, upload-time = "2026-04-01T14:43:37.213Z" },
554
+ { url = "https://files.pythonhosted.org/packages/10/e1/542a474affab20fd4a0f1836cb234e8493519da6b76899e30bcc5d990b8b/pillow-12.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414", size = 2463612, upload-time = "2026-04-01T14:43:39.421Z" },
555
+ { url = "https://files.pythonhosted.org/packages/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837, upload-time = "2026-04-01T14:43:41.506Z" },
556
+ { url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528, upload-time = "2026-04-01T14:43:43.773Z" },
557
+ { url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401, upload-time = "2026-04-01T14:43:45.87Z" },
558
+ { url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094, upload-time = "2026-04-01T14:43:48.438Z" },
559
+ { url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402, upload-time = "2026-04-01T14:43:51.292Z" },
560
+ { url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005, upload-time = "2026-04-01T14:43:54.242Z" },
561
+ { url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669, upload-time = "2026-04-01T14:43:57.335Z" },
562
+ { url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194, upload-time = "2026-04-01T14:43:59.864Z" },
563
+ { url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423, upload-time = "2026-04-01T14:44:02.74Z" },
564
+ { url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667, upload-time = "2026-04-01T14:44:05.381Z" },
565
+ { url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580, upload-time = "2026-04-01T14:44:08.39Z" },
566
+ { url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896, upload-time = "2026-04-01T14:44:11.197Z" },
567
+ { url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266, upload-time = "2026-04-01T14:44:13.947Z" },
568
+ { url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508, upload-time = "2026-04-01T14:44:16.312Z" },
569
+ { url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927, upload-time = "2026-04-01T14:44:18.89Z" },
570
+ { url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624, upload-time = "2026-04-01T14:44:21.115Z" },
571
+ { url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252, upload-time = "2026-04-01T14:44:23.663Z" },
572
+ { url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550, upload-time = "2026-04-01T14:44:26.772Z" },
573
+ { url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114, upload-time = "2026-04-01T14:44:29.615Z" },
574
+ { url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667, upload-time = "2026-04-01T14:44:32.773Z" },
575
+ { url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966, upload-time = "2026-04-01T14:44:35.252Z" },
576
+ { url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241, upload-time = "2026-04-01T14:44:37.875Z" },
577
+ { url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592, upload-time = "2026-04-01T14:44:40.336Z" },
578
+ { url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542, upload-time = "2026-04-01T14:44:43.251Z" },
579
+ { url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765, upload-time = "2026-04-01T14:44:45.996Z" },
580
+ { url = "https://files.pythonhosted.org/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848, upload-time = "2026-04-01T14:44:48.48Z" },
581
+ { url = "https://files.pythonhosted.org/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515, upload-time = "2026-04-01T14:44:51.353Z" },
582
+ { url = "https://files.pythonhosted.org/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159, upload-time = "2026-04-01T14:44:53.588Z" },
583
+ { url = "https://files.pythonhosted.org/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185, upload-time = "2026-04-01T14:44:56.039Z" },
584
+ { url = "https://files.pythonhosted.org/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386, upload-time = "2026-04-01T14:44:58.663Z" },
585
+ { url = "https://files.pythonhosted.org/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384, upload-time = "2026-04-01T14:45:01.5Z" },
586
+ { url = "https://files.pythonhosted.org/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599, upload-time = "2026-04-01T14:45:04.5Z" },
587
+ { url = "https://files.pythonhosted.org/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021, upload-time = "2026-04-01T14:45:07.117Z" },
588
+ { url = "https://files.pythonhosted.org/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360, upload-time = "2026-04-01T14:45:09.763Z" },
589
+ { url = "https://files.pythonhosted.org/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628, upload-time = "2026-04-01T14:45:12.378Z" },
590
+ { url = "https://files.pythonhosted.org/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321, upload-time = "2026-04-01T14:45:15.122Z" },
591
+ { url = "https://files.pythonhosted.org/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723, upload-time = "2026-04-01T14:45:17.797Z" },
592
+ { url = "https://files.pythonhosted.org/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400, upload-time = "2026-04-01T14:45:20.529Z" },
593
+ { url = "https://files.pythonhosted.org/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835, upload-time = "2026-04-01T14:45:23.162Z" },
594
+ { url = "https://files.pythonhosted.org/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225, upload-time = "2026-04-01T14:45:25.637Z" },
595
+ { url = "https://files.pythonhosted.org/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541, upload-time = "2026-04-01T14:45:28.355Z" },
596
+ { url = "https://files.pythonhosted.org/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251, upload-time = "2026-04-01T14:45:30.924Z" },
597
+ { url = "https://files.pythonhosted.org/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807, upload-time = "2026-04-01T14:45:33.908Z" },
598
+ { url = "https://files.pythonhosted.org/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935, upload-time = "2026-04-01T14:45:36.623Z" },
599
+ { url = "https://files.pythonhosted.org/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720, upload-time = "2026-04-01T14:45:39.258Z" },
600
+ { url = "https://files.pythonhosted.org/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498, upload-time = "2026-04-01T14:45:41.879Z" },
601
+ { url = "https://files.pythonhosted.org/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413, upload-time = "2026-04-01T14:45:44.705Z" },
602
+ { url = "https://files.pythonhosted.org/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084, upload-time = "2026-04-01T14:45:47.568Z" },
603
+ { url = "https://files.pythonhosted.org/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152, upload-time = "2026-04-01T14:45:50.032Z" },
604
+ { url = "https://files.pythonhosted.org/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579, upload-time = "2026-04-01T14:45:52.529Z" },
605
+ { url = "https://files.pythonhosted.org/packages/4e/b7/2437044fb910f499610356d1352e3423753c98e34f915252aafecc64889f/pillow-12.2.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0538bd5e05efec03ae613fd89c4ce0368ecd2ba239cc25b9f9be7ed426b0af1f", size = 5273969, upload-time = "2026-04-01T14:45:55.538Z" },
606
+ { url = "https://files.pythonhosted.org/packages/f6/f4/8316e31de11b780f4ac08ef3654a75555e624a98db1056ecb2122d008d5a/pillow-12.2.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:394167b21da716608eac917c60aa9b969421b5dcbbe02ae7f013e7b85811c69d", size = 4659674, upload-time = "2026-04-01T14:45:58.093Z" },
607
+ { url = "https://files.pythonhosted.org/packages/d4/37/664fca7201f8bb2aa1d20e2c3d5564a62e6ae5111741966c8319ca802361/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5d04bfa02cc2d23b497d1e90a0f927070043f6cbf303e738300532379a4b4e0f", size = 5288479, upload-time = "2026-04-01T14:46:01.141Z" },
608
+ { url = "https://files.pythonhosted.org/packages/49/62/5b0ed78fce87346be7a5cfcfaaad91f6a1f98c26f86bdbafa2066c647ef6/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0c838a5125cee37e68edec915651521191cef1e6aa336b855f495766e77a366e", size = 7032230, upload-time = "2026-04-01T14:46:03.874Z" },
609
+ { url = "https://files.pythonhosted.org/packages/c3/28/ec0fc38107fc32536908034e990c47914c57cd7c5a3ece4d8d8f7ffd7e27/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a6c9fa44005fa37a91ebfc95d081e8079757d2e904b27103f4f5fa6f0bf78c0", size = 5355404, upload-time = "2026-04-01T14:46:06.33Z" },
610
+ { url = "https://files.pythonhosted.org/packages/5e/8b/51b0eddcfa2180d60e41f06bd6d0a62202b20b59c68f5a132e615b75aecf/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:25373b66e0dd5905ed63fa3cae13c82fbddf3079f2c8bf15c6fb6a35586324c1", size = 6002215, upload-time = "2026-04-01T14:46:08.83Z" },
611
+ { url = "https://files.pythonhosted.org/packages/bc/60/5382c03e1970de634027cee8e1b7d39776b778b81812aaf45b694dfe9e28/pillow-12.2.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:bfa9c230d2fe991bed5318a5f119bd6780cda2915cca595393649fc118ab895e", size = 7080946, upload-time = "2026-04-01T14:46:11.734Z" },
612
+ ]
613
+
516
614
  [[package]]
517
615
  name = "platformdirs"
518
616
  version = "4.9.6"
@@ -888,10 +986,11 @@ wheels = [
888
986
 
889
987
  [[package]]
890
988
  name = "use-computer"
891
- version = "0.0.1"
989
+ version = "0.1.0"
892
990
  source = { editable = "." }
893
991
  dependencies = [
894
992
  { name = "httpx" },
993
+ { name = "pillow" },
895
994
  ]
896
995
 
897
996
  [package.dev-dependencies]
@@ -904,7 +1003,10 @@ dev = [
904
1003
  ]
905
1004
 
906
1005
  [package.metadata]
907
- requires-dist = [{ name = "httpx", specifier = ">=0.27" }]
1006
+ requires-dist = [
1007
+ { name = "httpx", specifier = ">=0.27" },
1008
+ { name = "pillow", specifier = ">=10" },
1009
+ ]
908
1010
 
909
1011
  [package.metadata.requires-dev]
910
1012
  dev = [
@@ -1,14 +0,0 @@
1
- from mmini.ios.apps import Apps, AsyncApps
2
- from mmini.ios.environment import AsyncEnvironment, Environment
3
- from mmini.ios.input import AsyncInput, Button, Input, Key
4
-
5
- __all__ = [
6
- "Apps",
7
- "AsyncApps",
8
- "AsyncEnvironment",
9
- "AsyncInput",
10
- "Button",
11
- "Environment",
12
- "Input",
13
- "Key",
14
- ]
@@ -1,4 +0,0 @@
1
- from mmini.macos.keyboard import AsyncKeyboard, Keyboard
2
- from mmini.macos.mouse import AsyncMouse, Mouse
3
-
4
- __all__ = ["AsyncKeyboard", "AsyncMouse", "Keyboard", "Mouse"]
@@ -1,15 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import mmini as _mmini
4
- from mmini import * # noqa: F403
5
-
6
-
7
- class Computer(_mmini.Mmini):
8
- """use.computer client."""
9
-
10
-
11
- class AsyncComputer(_mmini.AsyncMmini):
12
- """Async use.computer client."""
13
-
14
-
15
- __all__ = [*_mmini.__all__, "AsyncComputer", "Computer"]
@@ -1 +0,0 @@
1
-
File without changes
File without changes