cua-cli 0.1.1__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.
- cua_cli-0.1.1/.gitignore +214 -0
- cua_cli-0.1.1/PKG-INFO +141 -0
- cua_cli-0.1.1/README.md +100 -0
- cua_cli-0.1.1/cua_cli/__init__.py +3 -0
- cua_cli-0.1.1/cua_cli/api/__init__.py +1 -0
- cua_cli-0.1.1/cua_cli/api/client.py +142 -0
- cua_cli-0.1.1/cua_cli/auth/__init__.py +5 -0
- cua_cli-0.1.1/cua_cli/auth/browser.py +148 -0
- cua_cli-0.1.1/cua_cli/auth/store.py +168 -0
- cua_cli-0.1.1/cua_cli/commands/__init__.py +5 -0
- cua_cli-0.1.1/cua_cli/commands/auth.py +181 -0
- cua_cli-0.1.1/cua_cli/commands/image.py +673 -0
- cua_cli-0.1.1/cua_cli/commands/local_image.py +993 -0
- cua_cli-0.1.1/cua_cli/commands/mcp.py +844 -0
- cua_cli-0.1.1/cua_cli/commands/platform.py +309 -0
- cua_cli-0.1.1/cua_cli/commands/sandbox.py +541 -0
- cua_cli-0.1.1/cua_cli/commands/skills.py +929 -0
- cua_cli-0.1.1/cua_cli/main.py +88 -0
- cua_cli-0.1.1/cua_cli/utils/__init__.py +21 -0
- cua_cli-0.1.1/cua_cli/utils/async_utils.py +20 -0
- cua_cli-0.1.1/cua_cli/utils/docker.py +103 -0
- cua_cli-0.1.1/cua_cli/utils/output.py +66 -0
- cua_cli-0.1.1/cua_cli/utils/paths.py +30 -0
- cua_cli-0.1.1/cua_cli/utils/registry.py +148 -0
- cua_cli-0.1.1/pyproject.toml +111 -0
cua_cli-0.1.1/.gitignore
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
**/image/setup.iso
|
|
2
|
+
# Byte-compiled / optimized / DLL files
|
|
3
|
+
__pycache__/
|
|
4
|
+
*.py[cod]
|
|
5
|
+
*$py.class
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
node_modules/*
|
|
9
|
+
*/node_modules
|
|
10
|
+
**/node_modules
|
|
11
|
+
# Distribution / packaging
|
|
12
|
+
.Python
|
|
13
|
+
build/
|
|
14
|
+
!libs/lume/scripts/build/
|
|
15
|
+
develop-eggs/
|
|
16
|
+
dist/
|
|
17
|
+
downloads/
|
|
18
|
+
eggs/
|
|
19
|
+
.eggs/
|
|
20
|
+
lib/*
|
|
21
|
+
!libs/lumier/src/lib/
|
|
22
|
+
lib64/
|
|
23
|
+
parts/
|
|
24
|
+
sdist/
|
|
25
|
+
var/
|
|
26
|
+
wheels/
|
|
27
|
+
share/python-wheels/
|
|
28
|
+
*.egg-info/
|
|
29
|
+
.installed.cfg
|
|
30
|
+
*.egg
|
|
31
|
+
MANIFEST
|
|
32
|
+
# PyInstaller
|
|
33
|
+
# Usually these files are written by a python script from a template
|
|
34
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
35
|
+
*.manifest
|
|
36
|
+
*.spec
|
|
37
|
+
# Installer logs
|
|
38
|
+
pip-log.txt
|
|
39
|
+
pip-delete-this-directory.txt
|
|
40
|
+
# Unit test / coverage reports
|
|
41
|
+
htmlcov/
|
|
42
|
+
.tox/
|
|
43
|
+
.nox/
|
|
44
|
+
.coverage
|
|
45
|
+
.coverage.*
|
|
46
|
+
.cache
|
|
47
|
+
nosetests.xml
|
|
48
|
+
coverage.xml
|
|
49
|
+
*.cover
|
|
50
|
+
*.py,cover
|
|
51
|
+
.hypothesis/
|
|
52
|
+
.pytest_cache/
|
|
53
|
+
cover/
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
# Django stuff:
|
|
58
|
+
*.log
|
|
59
|
+
local_settings.py
|
|
60
|
+
db.sqlite3
|
|
61
|
+
db.sqlite3-journal
|
|
62
|
+
# Flask stuff:
|
|
63
|
+
instance/
|
|
64
|
+
.webassets-cache
|
|
65
|
+
# Scrapy stuff:
|
|
66
|
+
.scrapy
|
|
67
|
+
# Sphinx documentation
|
|
68
|
+
docs/_build/
|
|
69
|
+
# PyBuilder
|
|
70
|
+
.pybuilder/
|
|
71
|
+
target/
|
|
72
|
+
# Jupyter Notebook
|
|
73
|
+
.ipynb_checkpoints
|
|
74
|
+
# IPython
|
|
75
|
+
profile_default/
|
|
76
|
+
ipython_config.py
|
|
77
|
+
.pdm.toml
|
|
78
|
+
.pdm-python
|
|
79
|
+
.pdm-build/
|
|
80
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
81
|
+
__pypackages__/
|
|
82
|
+
# Celery stuff
|
|
83
|
+
celerybeat-schedule
|
|
84
|
+
celerybeat.pid
|
|
85
|
+
# SageMath parsed files
|
|
86
|
+
*.sage.py
|
|
87
|
+
# Environments
|
|
88
|
+
.env
|
|
89
|
+
.venv
|
|
90
|
+
env/
|
|
91
|
+
venv/
|
|
92
|
+
ENV/
|
|
93
|
+
env.bak/
|
|
94
|
+
venv.bak/
|
|
95
|
+
# Git worktrees
|
|
96
|
+
.worktrees/
|
|
97
|
+
# Spyder project settings
|
|
98
|
+
.spyderproject
|
|
99
|
+
.spyproject
|
|
100
|
+
# Rope project settings
|
|
101
|
+
.ropeproject
|
|
102
|
+
# mkdocs documentation
|
|
103
|
+
/site
|
|
104
|
+
# mypy
|
|
105
|
+
.mypy_cache/
|
|
106
|
+
.dmypy.json
|
|
107
|
+
dmypy.json
|
|
108
|
+
# Scripts
|
|
109
|
+
server/scripts/
|
|
110
|
+
# Pyre type checker
|
|
111
|
+
.pyre/
|
|
112
|
+
# pytype static type analyzer
|
|
113
|
+
.pytype/
|
|
114
|
+
# Cython debug symbols
|
|
115
|
+
cython_debug/
|
|
116
|
+
# Ruff stuff:
|
|
117
|
+
.ruff_cache/
|
|
118
|
+
# PyPI configuration file
|
|
119
|
+
.pypirc
|
|
120
|
+
# Conda
|
|
121
|
+
.conda/
|
|
122
|
+
# Local environment
|
|
123
|
+
.env.local
|
|
124
|
+
# macOS DS_Store
|
|
125
|
+
.DS_Store
|
|
126
|
+
weights/
|
|
127
|
+
weights/icon_detect/
|
|
128
|
+
weights/icon_detect/model.pt
|
|
129
|
+
weights/icon_detect/model.pt.zip
|
|
130
|
+
weights/icon_detect/model.pt.zip.part*
|
|
131
|
+
libs/python/omniparser/weights/icon_detect/model.pt
|
|
132
|
+
# Example test data and output
|
|
133
|
+
examples/test_data/
|
|
134
|
+
examples/output/
|
|
135
|
+
/screenshots/
|
|
136
|
+
/experiments/
|
|
137
|
+
/logs/
|
|
138
|
+
# Xcode
|
|
139
|
+
#
|
|
140
|
+
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
|
|
141
|
+
## User settings
|
|
142
|
+
xcuserdata/
|
|
143
|
+
## Obj-C/Swift specific
|
|
144
|
+
*.hmap
|
|
145
|
+
## App packaging
|
|
146
|
+
*.ipa
|
|
147
|
+
*.dSYM.zip
|
|
148
|
+
*.dSYM
|
|
149
|
+
## Playgrounds
|
|
150
|
+
timeline.xctimeline
|
|
151
|
+
playground.xcworkspace
|
|
152
|
+
# Swift Package Manager
|
|
153
|
+
#
|
|
154
|
+
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
|
|
155
|
+
# Packages/
|
|
156
|
+
# Package.pins
|
|
157
|
+
# Package.resolved
|
|
158
|
+
# *.xcodeproj
|
|
159
|
+
#
|
|
160
|
+
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
|
|
161
|
+
# hence it is not needed unless you have added a package configuration file to your project
|
|
162
|
+
.swiftpm/
|
|
163
|
+
.build/
|
|
164
|
+
# CocoaPods
|
|
165
|
+
#
|
|
166
|
+
# We recommend against adding the Pods directory to your .gitignore. However
|
|
167
|
+
# you should judge for yourself, the pros and cons are mentioned at:
|
|
168
|
+
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
|
169
|
+
#
|
|
170
|
+
# Pods/
|
|
171
|
+
#
|
|
172
|
+
# Add this line if you want to avoid checking in source code from the Xcode workspace
|
|
173
|
+
# *.xcworkspace
|
|
174
|
+
# Carthage
|
|
175
|
+
#
|
|
176
|
+
# Add this line if you want to avoid checking in source code from Carthage dependencies.
|
|
177
|
+
# Carthage/Checkouts
|
|
178
|
+
Carthage/Build/
|
|
179
|
+
# fastlane
|
|
180
|
+
#
|
|
181
|
+
# It is recommended to not store the screenshots in the git repo.
|
|
182
|
+
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
|
|
183
|
+
# For more information about the recommended setup visit:
|
|
184
|
+
# https://docs.fastlane.tools/best-practices/source-control/#source-control
|
|
185
|
+
fastlane/report.xml
|
|
186
|
+
fastlane/Preview.html
|
|
187
|
+
fastlane/screenshots/**/*.png
|
|
188
|
+
fastlane/test_output
|
|
189
|
+
# Ignore folder
|
|
190
|
+
ignore
|
|
191
|
+
# .release
|
|
192
|
+
.release/
|
|
193
|
+
# Shared folder
|
|
194
|
+
shared
|
|
195
|
+
# Trajectories
|
|
196
|
+
trajectories/
|
|
197
|
+
# Installation ID Storage
|
|
198
|
+
.storage/
|
|
199
|
+
# Gradio settings
|
|
200
|
+
.gradio_settings.json
|
|
201
|
+
# Lumier Storage
|
|
202
|
+
storage/
|
|
203
|
+
# Trashes
|
|
204
|
+
.Trashes
|
|
205
|
+
.Trash-1000/
|
|
206
|
+
post-provision
|
|
207
|
+
# Local secrets for act
|
|
208
|
+
.secrets
|
|
209
|
+
|
|
210
|
+
# Link checker scripts (dev tools)
|
|
211
|
+
scripts/check-repo-md-links.py
|
|
212
|
+
docs/scripts/check-links.py
|
|
213
|
+
docs/scripts/check-all-links.py
|
|
214
|
+
docs/scripts/check-mdx-links.py
|
cua_cli-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cua-cli
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Unified CLI for CUA - Computer-Use Agents
|
|
5
|
+
Project-URL: Homepage, https://github.com/trycua/cua
|
|
6
|
+
Project-URL: Documentation, https://docs.trycua.com
|
|
7
|
+
Project-URL: Repository, https://github.com/trycua/cua
|
|
8
|
+
Project-URL: Issues, https://github.com/trycua/cua/issues
|
|
9
|
+
Author-email: TryCua <hello@trycua.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
Keywords: agents,cli,cloud,computer-use,sandbox
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Requires-Python: <3.14,>=3.11
|
|
21
|
+
Requires-Dist: aiohttp>=3.9.0
|
|
22
|
+
Requires-Dist: cua-computer>=0.4.0
|
|
23
|
+
Requires-Dist: cua-core>=0.1.0
|
|
24
|
+
Requires-Dist: pillow>=10.0.0
|
|
25
|
+
Requires-Dist: rich>=13.0.0
|
|
26
|
+
Requires-Dist: websockets>=12.0
|
|
27
|
+
Provides-Extra: all
|
|
28
|
+
Requires-Dist: fastmcp>=2.0; extra == 'all'
|
|
29
|
+
Requires-Dist: litellm>=1.74.0; extra == 'all'
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: respx>=0.20.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
36
|
+
Provides-Extra: mcp
|
|
37
|
+
Requires-Dist: fastmcp>=2.0; extra == 'mcp'
|
|
38
|
+
Provides-Extra: skills
|
|
39
|
+
Requires-Dist: litellm>=1.74.0; extra == 'skills'
|
|
40
|
+
Description-Content-Type: text/markdown
|
|
41
|
+
|
|
42
|
+
# CUA CLI
|
|
43
|
+
|
|
44
|
+
Unified command-line interface for CUA (Computer-Use Agents).
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install cua-cli
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Usage
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Authentication
|
|
56
|
+
cua auth login # Authenticate via browser
|
|
57
|
+
cua auth login --api-key # Authenticate with API key
|
|
58
|
+
cua auth logout # Clear credentials
|
|
59
|
+
cua auth env # Export API key to .env file
|
|
60
|
+
|
|
61
|
+
# Sandbox Management
|
|
62
|
+
cua sb list # List all sandboxes
|
|
63
|
+
cua sb create --os linux --size medium --region north-america
|
|
64
|
+
cua sb get <name> # Get sandbox details
|
|
65
|
+
cua sb start <name> # Start a stopped sandbox
|
|
66
|
+
cua sb stop <name> # Stop a running sandbox
|
|
67
|
+
cua sb restart <name> # Restart a sandbox
|
|
68
|
+
cua sb suspend <name> # Suspend a sandbox
|
|
69
|
+
cua sb delete <name> # Delete a sandbox
|
|
70
|
+
cua sb vnc <name> # Open sandbox in browser
|
|
71
|
+
|
|
72
|
+
# Image Management
|
|
73
|
+
cua image list # List cloud images
|
|
74
|
+
cua image list --local # List local images
|
|
75
|
+
cua image push <name> # Upload image to cloud
|
|
76
|
+
cua image pull <name> # Download image from cloud
|
|
77
|
+
cua image delete <name> # Delete cloud image
|
|
78
|
+
|
|
79
|
+
# Skills Management
|
|
80
|
+
cua skills list # List recorded skills
|
|
81
|
+
cua skills read <name> # Read a skill's content
|
|
82
|
+
cua skills record <name> # Record a new skill
|
|
83
|
+
cua skills replay <name> # Replay a skill
|
|
84
|
+
cua skills delete <name> # Delete a skill
|
|
85
|
+
cua skills clean # Delete all skills
|
|
86
|
+
|
|
87
|
+
# MCP Server (for AI assistants)
|
|
88
|
+
cua serve-mcp # Start MCP server with all permissions
|
|
89
|
+
cua serve-mcp --permissions sandbox:all,computer:readonly
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Installation Options
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# Basic installation
|
|
96
|
+
pip install cua-cli
|
|
97
|
+
|
|
98
|
+
# With MCP server support
|
|
99
|
+
pip install cua-cli[mcp]
|
|
100
|
+
|
|
101
|
+
# With skills recording (VLM captioning)
|
|
102
|
+
pip install cua-cli[skills]
|
|
103
|
+
|
|
104
|
+
# Full installation
|
|
105
|
+
pip install cua-cli[all]
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## MCP Integration
|
|
109
|
+
|
|
110
|
+
To use CUA with Claude Code or other MCP-compatible AI assistants:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# Add CUA as an MCP server
|
|
114
|
+
claude mcp add cua -- cua serve-mcp
|
|
115
|
+
|
|
116
|
+
# With specific permissions
|
|
117
|
+
claude mcp add cua -- cua serve-mcp --permissions sandbox:all,computer:readonly
|
|
118
|
+
|
|
119
|
+
# With a default sandbox
|
|
120
|
+
claude mcp add cua -- cua serve-mcp --sandbox my-sandbox
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Available Permissions
|
|
124
|
+
|
|
125
|
+
- `all` - All permissions
|
|
126
|
+
- `sandbox:all` - Full sandbox management
|
|
127
|
+
- `sandbox:readonly` - List and get sandboxes only
|
|
128
|
+
- `computer:all` - Full computer control
|
|
129
|
+
- `computer:readonly` - Screenshots only
|
|
130
|
+
- `skills:all` - Full skills management
|
|
131
|
+
- `skills:readonly` - List and read skills only
|
|
132
|
+
|
|
133
|
+
Individual permissions: `sandbox:list`, `sandbox:create`, `sandbox:delete`, `sandbox:start`, `sandbox:stop`, `sandbox:restart`, `sandbox:suspend`, `sandbox:get`, `sandbox:vnc`, `computer:screenshot`, `computer:click`, `computer:type`, `computer:key`, `computer:scroll`, `computer:drag`, `computer:hotkey`, `computer:clipboard`, `computer:file`, `computer:shell`, `computer:window`, `skills:list`, `skills:read`, `skills:record`, `skills:delete`
|
|
134
|
+
|
|
135
|
+
## Environment Variables
|
|
136
|
+
|
|
137
|
+
- `CUA_API_KEY`: API key for authentication
|
|
138
|
+
- `CUA_API_BASE`: API base URL (default: https://api.cua.ai)
|
|
139
|
+
- `CUA_WEBSITE_URL`: Website URL for OAuth (default: https://cua.ai)
|
|
140
|
+
- `CUA_MCP_PERMISSIONS`: Default MCP permissions (comma-separated)
|
|
141
|
+
- `CUA_SANDBOX`: Default sandbox name for computer commands
|
cua_cli-0.1.1/README.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# CUA CLI
|
|
2
|
+
|
|
3
|
+
Unified command-line interface for CUA (Computer-Use Agents).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install cua-cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Authentication
|
|
15
|
+
cua auth login # Authenticate via browser
|
|
16
|
+
cua auth login --api-key # Authenticate with API key
|
|
17
|
+
cua auth logout # Clear credentials
|
|
18
|
+
cua auth env # Export API key to .env file
|
|
19
|
+
|
|
20
|
+
# Sandbox Management
|
|
21
|
+
cua sb list # List all sandboxes
|
|
22
|
+
cua sb create --os linux --size medium --region north-america
|
|
23
|
+
cua sb get <name> # Get sandbox details
|
|
24
|
+
cua sb start <name> # Start a stopped sandbox
|
|
25
|
+
cua sb stop <name> # Stop a running sandbox
|
|
26
|
+
cua sb restart <name> # Restart a sandbox
|
|
27
|
+
cua sb suspend <name> # Suspend a sandbox
|
|
28
|
+
cua sb delete <name> # Delete a sandbox
|
|
29
|
+
cua sb vnc <name> # Open sandbox in browser
|
|
30
|
+
|
|
31
|
+
# Image Management
|
|
32
|
+
cua image list # List cloud images
|
|
33
|
+
cua image list --local # List local images
|
|
34
|
+
cua image push <name> # Upload image to cloud
|
|
35
|
+
cua image pull <name> # Download image from cloud
|
|
36
|
+
cua image delete <name> # Delete cloud image
|
|
37
|
+
|
|
38
|
+
# Skills Management
|
|
39
|
+
cua skills list # List recorded skills
|
|
40
|
+
cua skills read <name> # Read a skill's content
|
|
41
|
+
cua skills record <name> # Record a new skill
|
|
42
|
+
cua skills replay <name> # Replay a skill
|
|
43
|
+
cua skills delete <name> # Delete a skill
|
|
44
|
+
cua skills clean # Delete all skills
|
|
45
|
+
|
|
46
|
+
# MCP Server (for AI assistants)
|
|
47
|
+
cua serve-mcp # Start MCP server with all permissions
|
|
48
|
+
cua serve-mcp --permissions sandbox:all,computer:readonly
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Installation Options
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Basic installation
|
|
55
|
+
pip install cua-cli
|
|
56
|
+
|
|
57
|
+
# With MCP server support
|
|
58
|
+
pip install cua-cli[mcp]
|
|
59
|
+
|
|
60
|
+
# With skills recording (VLM captioning)
|
|
61
|
+
pip install cua-cli[skills]
|
|
62
|
+
|
|
63
|
+
# Full installation
|
|
64
|
+
pip install cua-cli[all]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## MCP Integration
|
|
68
|
+
|
|
69
|
+
To use CUA with Claude Code or other MCP-compatible AI assistants:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Add CUA as an MCP server
|
|
73
|
+
claude mcp add cua -- cua serve-mcp
|
|
74
|
+
|
|
75
|
+
# With specific permissions
|
|
76
|
+
claude mcp add cua -- cua serve-mcp --permissions sandbox:all,computer:readonly
|
|
77
|
+
|
|
78
|
+
# With a default sandbox
|
|
79
|
+
claude mcp add cua -- cua serve-mcp --sandbox my-sandbox
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Available Permissions
|
|
83
|
+
|
|
84
|
+
- `all` - All permissions
|
|
85
|
+
- `sandbox:all` - Full sandbox management
|
|
86
|
+
- `sandbox:readonly` - List and get sandboxes only
|
|
87
|
+
- `computer:all` - Full computer control
|
|
88
|
+
- `computer:readonly` - Screenshots only
|
|
89
|
+
- `skills:all` - Full skills management
|
|
90
|
+
- `skills:readonly` - List and read skills only
|
|
91
|
+
|
|
92
|
+
Individual permissions: `sandbox:list`, `sandbox:create`, `sandbox:delete`, `sandbox:start`, `sandbox:stop`, `sandbox:restart`, `sandbox:suspend`, `sandbox:get`, `sandbox:vnc`, `computer:screenshot`, `computer:click`, `computer:type`, `computer:key`, `computer:scroll`, `computer:drag`, `computer:hotkey`, `computer:clipboard`, `computer:file`, `computer:shell`, `computer:window`, `skills:list`, `skills:read`, `skills:record`, `skills:delete`
|
|
93
|
+
|
|
94
|
+
## Environment Variables
|
|
95
|
+
|
|
96
|
+
- `CUA_API_KEY`: API key for authentication
|
|
97
|
+
- `CUA_API_BASE`: API base URL (default: https://api.cua.ai)
|
|
98
|
+
- `CUA_WEBSITE_URL`: Website URL for OAuth (default: https://cua.ai)
|
|
99
|
+
- `CUA_MCP_PERMISSIONS`: Default MCP permissions (comma-separated)
|
|
100
|
+
- `CUA_SANDBOX`: Default sandbox name for computer commands
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""API client module for CUA CLI."""
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""HTTP API client for CUA cloud services."""
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import os
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any, Optional
|
|
7
|
+
from urllib.parse import quote
|
|
8
|
+
|
|
9
|
+
import aiohttp
|
|
10
|
+
from cua_cli.auth.store import require_api_key
|
|
11
|
+
|
|
12
|
+
DEFAULT_API_BASE = "https://api.cua.ai"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_api_base() -> str:
|
|
16
|
+
"""Get the API base URL."""
|
|
17
|
+
return os.environ.get("CUA_API_BASE", DEFAULT_API_BASE).rstrip("/")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CloudAPIClient:
|
|
21
|
+
"""HTTP client for CUA cloud API."""
|
|
22
|
+
|
|
23
|
+
def __init__(self, api_key: Optional[str] = None, api_base: Optional[str] = None):
|
|
24
|
+
self.api_key = api_key or require_api_key()
|
|
25
|
+
self.api_base = api_base or get_api_base()
|
|
26
|
+
|
|
27
|
+
def _headers(self) -> dict[str, str]:
|
|
28
|
+
return {
|
|
29
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
30
|
+
"Accept": "application/json",
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async def _request(
|
|
34
|
+
self,
|
|
35
|
+
method: str,
|
|
36
|
+
path: str,
|
|
37
|
+
json: Optional[dict] = None,
|
|
38
|
+
timeout: int = 30,
|
|
39
|
+
) -> tuple[int, Any]:
|
|
40
|
+
"""Make an HTTP request to the API.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
Tuple of (status_code, response_data)
|
|
44
|
+
"""
|
|
45
|
+
url = f"{self.api_base}{path}"
|
|
46
|
+
headers = self._headers()
|
|
47
|
+
|
|
48
|
+
if json is not None:
|
|
49
|
+
headers["Content-Type"] = "application/json"
|
|
50
|
+
|
|
51
|
+
async with aiohttp.ClientSession() as session:
|
|
52
|
+
timeout_obj = aiohttp.ClientTimeout(total=timeout)
|
|
53
|
+
async with session.request(
|
|
54
|
+
method, url, headers=headers, json=json, timeout=timeout_obj
|
|
55
|
+
) as resp:
|
|
56
|
+
try:
|
|
57
|
+
data = await resp.json(content_type=None)
|
|
58
|
+
except Exception:
|
|
59
|
+
data = await resp.text()
|
|
60
|
+
return resp.status, data
|
|
61
|
+
|
|
62
|
+
# Image API methods
|
|
63
|
+
|
|
64
|
+
async def list_images(self) -> list[dict[str, Any]]:
|
|
65
|
+
"""List all images in the workspace."""
|
|
66
|
+
status, data = await self._request("GET", "/v1/images")
|
|
67
|
+
if status == 200 and isinstance(data, list):
|
|
68
|
+
return data
|
|
69
|
+
return []
|
|
70
|
+
|
|
71
|
+
async def initiate_upload(
|
|
72
|
+
self,
|
|
73
|
+
name: str,
|
|
74
|
+
tag: str,
|
|
75
|
+
image_type: str,
|
|
76
|
+
size_bytes: int,
|
|
77
|
+
checksum_sha256: str,
|
|
78
|
+
) -> tuple[int, dict[str, Any]]:
|
|
79
|
+
"""Initiate a multi-part upload session.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Tuple of (status_code, session_data)
|
|
83
|
+
"""
|
|
84
|
+
path = f"/v1/images/{quote(name, safe='')}/upload"
|
|
85
|
+
body = {
|
|
86
|
+
"tag": tag,
|
|
87
|
+
"image_type": image_type,
|
|
88
|
+
"size_bytes": size_bytes,
|
|
89
|
+
"checksum_sha256": checksum_sha256,
|
|
90
|
+
}
|
|
91
|
+
return await self._request("POST", path, json=body)
|
|
92
|
+
|
|
93
|
+
async def get_upload_part_url(
|
|
94
|
+
self, name: str, upload_id: str, part_number: int
|
|
95
|
+
) -> tuple[int, dict[str, Any]]:
|
|
96
|
+
"""Get a signed URL for uploading a part."""
|
|
97
|
+
path = f"/v1/images/{quote(name, safe='')}/upload/{upload_id}/part/{part_number}"
|
|
98
|
+
return await self._request("GET", path)
|
|
99
|
+
|
|
100
|
+
async def complete_upload(
|
|
101
|
+
self, name: str, upload_id: str, parts: list[dict[str, Any]]
|
|
102
|
+
) -> tuple[int, dict[str, Any]]:
|
|
103
|
+
"""Complete a multi-part upload."""
|
|
104
|
+
path = f"/v1/images/{quote(name, safe='')}/upload/{upload_id}/complete"
|
|
105
|
+
return await self._request("POST", path, json={"parts": parts})
|
|
106
|
+
|
|
107
|
+
async def abort_upload(self, name: str, upload_id: str) -> tuple[int, Any]:
|
|
108
|
+
"""Abort an upload session."""
|
|
109
|
+
path = f"/v1/images/{quote(name, safe='')}/upload/{upload_id}"
|
|
110
|
+
return await self._request("DELETE", path)
|
|
111
|
+
|
|
112
|
+
async def get_download_url(self, name: str, tag: str) -> tuple[int, dict[str, Any]]:
|
|
113
|
+
"""Get a signed URL for downloading an image."""
|
|
114
|
+
path = f"/v1/images/{quote(name, safe='')}/download?tag={quote(tag, safe='')}"
|
|
115
|
+
return await self._request("GET", path)
|
|
116
|
+
|
|
117
|
+
async def delete_image(self, name: str, tag: str) -> tuple[int, Any]:
|
|
118
|
+
"""Delete an image version."""
|
|
119
|
+
path = f"/v1/images/{quote(name, safe='')}?tag={quote(tag, safe='')}"
|
|
120
|
+
return await self._request("DELETE", path)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def calculate_file_hash(file_path: Path) -> str:
|
|
124
|
+
"""Calculate SHA256 hash of a file."""
|
|
125
|
+
sha256 = hashlib.sha256()
|
|
126
|
+
with open(file_path, "rb") as f:
|
|
127
|
+
for chunk in iter(lambda: f.read(8192), b""):
|
|
128
|
+
sha256.update(chunk)
|
|
129
|
+
return sha256.hexdigest()
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def format_bytes(size_bytes: int) -> str:
|
|
133
|
+
"""Format bytes in human-readable format."""
|
|
134
|
+
if size_bytes == 0:
|
|
135
|
+
return "0 B"
|
|
136
|
+
units = ["B", "KB", "MB", "GB", "TB"]
|
|
137
|
+
k = 1024
|
|
138
|
+
i = 0
|
|
139
|
+
while size_bytes >= k and i < len(units) - 1:
|
|
140
|
+
size_bytes /= k
|
|
141
|
+
i += 1
|
|
142
|
+
return f"{size_bytes:.2f} {units[i]}"
|