This is page 3 of 28. Use http://codebase.md/trycua/cua?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .cursorignore
├── .dockerignore
├── .editorconfig
├── .gitattributes
├── .github
│ ├── FUNDING.yml
│ ├── scripts
│ │ ├── get_pyproject_version.py
│ │ └── tests
│ │ ├── __init__.py
│ │ ├── README.md
│ │ └── test_get_pyproject_version.py
│ └── workflows
│ ├── bump-version.yml
│ ├── ci-lume.yml
│ ├── docker-publish-cua-linux.yml
│ ├── docker-publish-cua-windows.yml
│ ├── docker-publish-kasm.yml
│ ├── docker-publish-xfce.yml
│ ├── docker-reusable-publish.yml
│ ├── link-check.yml
│ ├── lint.yml
│ ├── npm-publish-cli.yml
│ ├── npm-publish-computer.yml
│ ├── npm-publish-core.yml
│ ├── publish-lume.yml
│ ├── pypi-publish-agent.yml
│ ├── pypi-publish-computer-server.yml
│ ├── pypi-publish-computer.yml
│ ├── pypi-publish-core.yml
│ ├── pypi-publish-mcp-server.yml
│ ├── pypi-publish-som.yml
│ ├── pypi-reusable-publish.yml
│ ├── python-tests.yml
│ ├── test-cua-models.yml
│ └── test-validation-script.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .prettierignore
├── .prettierrc.yaml
├── .vscode
│ ├── docs.code-workspace
│ ├── extensions.json
│ ├── launch.json
│ ├── libs-ts.code-workspace
│ ├── lume.code-workspace
│ ├── lumier.code-workspace
│ ├── py.code-workspace
│ └── settings.json
├── blog
│ ├── app-use.md
│ ├── assets
│ │ ├── composite-agents.png
│ │ ├── docker-ubuntu-support.png
│ │ ├── hack-booth.png
│ │ ├── hack-closing-ceremony.jpg
│ │ ├── hack-cua-ollama-hud.jpeg
│ │ ├── hack-leaderboard.png
│ │ ├── hack-the-north.png
│ │ ├── hack-winners.jpeg
│ │ ├── hack-workshop.jpeg
│ │ ├── hud-agent-evals.png
│ │ └── trajectory-viewer.jpeg
│ ├── bringing-computer-use-to-the-web.md
│ ├── build-your-own-operator-on-macos-1.md
│ ├── build-your-own-operator-on-macos-2.md
│ ├── cloud-windows-ga-macos-preview.md
│ ├── composite-agents.md
│ ├── computer-use-agents-for-growth-hacking.md
│ ├── cua-hackathon.md
│ ├── cua-playground-preview.md
│ ├── cua-vlm-router.md
│ ├── hack-the-north.md
│ ├── hud-agent-evals.md
│ ├── human-in-the-loop.md
│ ├── introducing-cua-cli.md
│ ├── introducing-cua-cloud-containers.md
│ ├── lume-to-containerization.md
│ ├── neurips-2025-cua-papers.md
│ ├── sandboxed-python-execution.md
│ ├── training-computer-use-models-trajectories-1.md
│ ├── trajectory-viewer.md
│ ├── ubuntu-docker-support.md
│ └── windows-sandbox.md
├── CONTRIBUTING.md
├── Development.md
├── Dockerfile
├── docs
│ ├── .env.example
│ ├── .gitignore
│ ├── content
│ │ └── docs
│ │ ├── agent-sdk
│ │ │ ├── agent-loops.mdx
│ │ │ ├── benchmarks
│ │ │ │ ├── index.mdx
│ │ │ │ ├── interactive.mdx
│ │ │ │ ├── introduction.mdx
│ │ │ │ ├── meta.json
│ │ │ │ ├── osworld-verified.mdx
│ │ │ │ ├── screenspot-pro.mdx
│ │ │ │ └── screenspot-v2.mdx
│ │ │ ├── callbacks
│ │ │ │ ├── agent-lifecycle.mdx
│ │ │ │ ├── cost-saving.mdx
│ │ │ │ ├── index.mdx
│ │ │ │ ├── logging.mdx
│ │ │ │ ├── meta.json
│ │ │ │ ├── pii-anonymization.mdx
│ │ │ │ └── trajectories.mdx
│ │ │ ├── chat-history.mdx
│ │ │ ├── custom-tools.mdx
│ │ │ ├── customizing-computeragent.mdx
│ │ │ ├── integrations
│ │ │ │ ├── hud.mdx
│ │ │ │ ├── meta.json
│ │ │ │ └── observability.mdx
│ │ │ ├── mcp-server
│ │ │ │ ├── client-integrations.mdx
│ │ │ │ ├── configuration.mdx
│ │ │ │ ├── index.mdx
│ │ │ │ ├── installation.mdx
│ │ │ │ ├── llm-integrations.mdx
│ │ │ │ ├── meta.json
│ │ │ │ ├── tools.mdx
│ │ │ │ └── usage.mdx
│ │ │ ├── message-format.mdx
│ │ │ ├── meta.json
│ │ │ ├── migration-guide.mdx
│ │ │ ├── prompt-caching.mdx
│ │ │ ├── supported-agents
│ │ │ │ ├── composed-agents.mdx
│ │ │ │ ├── computer-use-agents.mdx
│ │ │ │ ├── grounding-models.mdx
│ │ │ │ ├── human-in-the-loop.mdx
│ │ │ │ └── meta.json
│ │ │ ├── supported-model-providers
│ │ │ │ ├── cua-vlm-router.mdx
│ │ │ │ ├── index.mdx
│ │ │ │ └── local-models.mdx
│ │ │ ├── telemetry.mdx
│ │ │ └── usage-tracking.mdx
│ │ ├── cli-playbook
│ │ │ ├── commands.mdx
│ │ │ ├── index.mdx
│ │ │ └── meta.json
│ │ ├── computer-sdk
│ │ │ ├── cloud-vm-management.mdx
│ │ │ ├── commands.mdx
│ │ │ ├── computer-server
│ │ │ │ ├── Commands.mdx
│ │ │ │ ├── index.mdx
│ │ │ │ ├── meta.json
│ │ │ │ ├── REST-API.mdx
│ │ │ │ └── WebSocket-API.mdx
│ │ │ ├── computer-ui.mdx
│ │ │ ├── computers.mdx
│ │ │ ├── custom-computer-handlers.mdx
│ │ │ ├── meta.json
│ │ │ ├── sandboxed-python.mdx
│ │ │ └── tracing-api.mdx
│ │ ├── example-usecases
│ │ │ ├── form-filling.mdx
│ │ │ ├── gemini-complex-ui-navigation.mdx
│ │ │ ├── meta.json
│ │ │ ├── post-event-contact-export.mdx
│ │ │ └── windows-app-behind-vpn.mdx
│ │ ├── get-started
│ │ │ ├── meta.json
│ │ │ └── quickstart.mdx
│ │ ├── index.mdx
│ │ ├── macos-vm-cli-playbook
│ │ │ ├── lume
│ │ │ │ ├── cli-reference.mdx
│ │ │ │ ├── faq.md
│ │ │ │ ├── http-api.mdx
│ │ │ │ ├── index.mdx
│ │ │ │ ├── installation.mdx
│ │ │ │ ├── meta.json
│ │ │ │ └── prebuilt-images.mdx
│ │ │ ├── lumier
│ │ │ │ ├── building-lumier.mdx
│ │ │ │ ├── docker-compose.mdx
│ │ │ │ ├── docker.mdx
│ │ │ │ ├── index.mdx
│ │ │ │ ├── installation.mdx
│ │ │ │ └── meta.json
│ │ │ └── meta.json
│ │ └── meta.json
│ ├── next.config.mjs
│ ├── package-lock.json
│ ├── package.json
│ ├── pnpm-lock.yaml
│ ├── postcss.config.mjs
│ ├── public
│ │ └── img
│ │ ├── agent_gradio_ui.png
│ │ ├── agent.png
│ │ ├── bg-dark.jpg
│ │ ├── bg-light.jpg
│ │ ├── cli.png
│ │ ├── computer.png
│ │ ├── grounding-with-gemini3.gif
│ │ ├── hero.png
│ │ ├── laminar_trace_example.png
│ │ ├── som_box_threshold.png
│ │ └── som_iou_threshold.png
│ ├── README.md
│ ├── source.config.ts
│ ├── src
│ │ ├── app
│ │ │ ├── (home)
│ │ │ │ ├── [[...slug]]
│ │ │ │ │ └── page.tsx
│ │ │ │ └── layout.tsx
│ │ │ ├── api
│ │ │ │ ├── posthog
│ │ │ │ │ └── [...path]
│ │ │ │ │ └── route.ts
│ │ │ │ └── search
│ │ │ │ └── route.ts
│ │ │ ├── favicon.ico
│ │ │ ├── global.css
│ │ │ ├── layout.config.tsx
│ │ │ ├── layout.tsx
│ │ │ ├── llms.mdx
│ │ │ │ └── [[...slug]]
│ │ │ │ └── route.ts
│ │ │ ├── llms.txt
│ │ │ │ └── route.ts
│ │ │ ├── robots.ts
│ │ │ └── sitemap.ts
│ │ ├── assets
│ │ │ ├── discord-black.svg
│ │ │ ├── discord-white.svg
│ │ │ ├── logo-black.svg
│ │ │ └── logo-white.svg
│ │ ├── components
│ │ │ ├── analytics-tracker.tsx
│ │ │ ├── cookie-consent.tsx
│ │ │ ├── doc-actions-menu.tsx
│ │ │ ├── editable-code-block.tsx
│ │ │ ├── footer.tsx
│ │ │ ├── hero.tsx
│ │ │ ├── iou.tsx
│ │ │ ├── mermaid.tsx
│ │ │ └── page-feedback.tsx
│ │ ├── lib
│ │ │ ├── llms.ts
│ │ │ └── source.ts
│ │ ├── mdx-components.tsx
│ │ └── providers
│ │ └── posthog-provider.tsx
│ └── tsconfig.json
├── examples
│ ├── agent_examples.py
│ ├── agent_ui_examples.py
│ ├── browser_tool_example.py
│ ├── cloud_api_examples.py
│ ├── computer_examples_windows.py
│ ├── computer_examples.py
│ ├── computer_ui_examples.py
│ ├── computer-example-ts
│ │ ├── .env.example
│ │ ├── .gitignore
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ ├── pnpm-lock.yaml
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── helpers.ts
│ │ │ └── index.ts
│ │ └── tsconfig.json
│ ├── docker_examples.py
│ ├── evals
│ │ ├── hud_eval_examples.py
│ │ └── wikipedia_most_linked.txt
│ ├── pylume_examples.py
│ ├── sandboxed_functions_examples.py
│ ├── som_examples.py
│ ├── tracing_examples.py
│ ├── utils.py
│ └── winsandbox_example.py
├── img
│ ├── agent_gradio_ui.png
│ ├── agent.png
│ ├── cli.png
│ ├── computer.png
│ ├── logo_black.png
│ └── logo_white.png
├── libs
│ ├── kasm
│ │ ├── Dockerfile
│ │ ├── LICENSE
│ │ ├── README.md
│ │ └── src
│ │ └── ubuntu
│ │ └── install
│ │ └── firefox
│ │ ├── custom_startup.sh
│ │ ├── firefox.desktop
│ │ └── install_firefox.sh
│ ├── lume
│ │ ├── .cursorignore
│ │ ├── CONTRIBUTING.md
│ │ ├── Development.md
│ │ ├── img
│ │ │ └── cli.png
│ │ ├── Package.resolved
│ │ ├── Package.swift
│ │ ├── README.md
│ │ ├── resources
│ │ │ └── lume.entitlements
│ │ ├── scripts
│ │ │ ├── build
│ │ │ │ ├── build-debug.sh
│ │ │ │ ├── build-release-notarized.sh
│ │ │ │ └── build-release.sh
│ │ │ └── install.sh
│ │ ├── src
│ │ │ ├── Commands
│ │ │ │ ├── Clone.swift
│ │ │ │ ├── Config.swift
│ │ │ │ ├── Create.swift
│ │ │ │ ├── Delete.swift
│ │ │ │ ├── Get.swift
│ │ │ │ ├── Images.swift
│ │ │ │ ├── IPSW.swift
│ │ │ │ ├── List.swift
│ │ │ │ ├── Logs.swift
│ │ │ │ ├── Options
│ │ │ │ │ └── FormatOption.swift
│ │ │ │ ├── Prune.swift
│ │ │ │ ├── Pull.swift
│ │ │ │ ├── Push.swift
│ │ │ │ ├── Run.swift
│ │ │ │ ├── Serve.swift
│ │ │ │ ├── Set.swift
│ │ │ │ └── Stop.swift
│ │ │ ├── ContainerRegistry
│ │ │ │ ├── ImageContainerRegistry.swift
│ │ │ │ ├── ImageList.swift
│ │ │ │ └── ImagesPrinter.swift
│ │ │ ├── Errors
│ │ │ │ └── Errors.swift
│ │ │ ├── FileSystem
│ │ │ │ ├── Home.swift
│ │ │ │ ├── Settings.swift
│ │ │ │ ├── VMConfig.swift
│ │ │ │ ├── VMDirectory.swift
│ │ │ │ └── VMLocation.swift
│ │ │ ├── LumeController.swift
│ │ │ ├── Main.swift
│ │ │ ├── Server
│ │ │ │ ├── Handlers.swift
│ │ │ │ ├── HTTP.swift
│ │ │ │ ├── Requests.swift
│ │ │ │ ├── Responses.swift
│ │ │ │ └── Server.swift
│ │ │ ├── Utils
│ │ │ │ ├── CommandRegistry.swift
│ │ │ │ ├── CommandUtils.swift
│ │ │ │ ├── Logger.swift
│ │ │ │ ├── NetworkUtils.swift
│ │ │ │ ├── Path.swift
│ │ │ │ ├── ProcessRunner.swift
│ │ │ │ ├── ProgressLogger.swift
│ │ │ │ ├── String.swift
│ │ │ │ └── Utils.swift
│ │ │ ├── Virtualization
│ │ │ │ ├── DarwinImageLoader.swift
│ │ │ │ ├── DHCPLeaseParser.swift
│ │ │ │ ├── ImageLoaderFactory.swift
│ │ │ │ └── VMVirtualizationService.swift
│ │ │ ├── VM
│ │ │ │ ├── DarwinVM.swift
│ │ │ │ ├── LinuxVM.swift
│ │ │ │ ├── VM.swift
│ │ │ │ ├── VMDetails.swift
│ │ │ │ ├── VMDetailsPrinter.swift
│ │ │ │ ├── VMDisplayResolution.swift
│ │ │ │ └── VMFactory.swift
│ │ │ └── VNC
│ │ │ ├── PassphraseGenerator.swift
│ │ │ └── VNCService.swift
│ │ └── tests
│ │ ├── Mocks
│ │ │ ├── MockVM.swift
│ │ │ ├── MockVMVirtualizationService.swift
│ │ │ └── MockVNCService.swift
│ │ ├── VM
│ │ │ └── VMDetailsPrinterTests.swift
│ │ ├── VMTests.swift
│ │ ├── VMVirtualizationServiceTests.swift
│ │ └── VNCServiceTests.swift
│ ├── lumier
│ │ ├── .dockerignore
│ │ ├── Dockerfile
│ │ ├── README.md
│ │ └── src
│ │ ├── bin
│ │ │ └── entry.sh
│ │ ├── config
│ │ │ └── constants.sh
│ │ ├── hooks
│ │ │ └── on-logon.sh
│ │ └── lib
│ │ ├── utils.sh
│ │ └── vm.sh
│ ├── python
│ │ ├── agent
│ │ │ ├── .bumpversion.cfg
│ │ │ ├── agent
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __main__.py
│ │ │ │ ├── adapters
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── cua_adapter.py
│ │ │ │ │ ├── huggingfacelocal_adapter.py
│ │ │ │ │ ├── human_adapter.py
│ │ │ │ │ ├── mlxvlm_adapter.py
│ │ │ │ │ └── models
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── generic.py
│ │ │ │ │ ├── internvl.py
│ │ │ │ │ ├── opencua.py
│ │ │ │ │ └── qwen2_5_vl.py
│ │ │ │ ├── agent.py
│ │ │ │ ├── callbacks
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── budget_manager.py
│ │ │ │ │ ├── image_retention.py
│ │ │ │ │ ├── logging.py
│ │ │ │ │ ├── operator_validator.py
│ │ │ │ │ ├── pii_anonymization.py
│ │ │ │ │ ├── prompt_instructions.py
│ │ │ │ │ ├── telemetry.py
│ │ │ │ │ └── trajectory_saver.py
│ │ │ │ ├── cli.py
│ │ │ │ ├── computers
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── cua.py
│ │ │ │ │ └── custom.py
│ │ │ │ ├── decorators.py
│ │ │ │ ├── human_tool
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ ├── server.py
│ │ │ │ │ └── ui.py
│ │ │ │ ├── integrations
│ │ │ │ │ └── hud
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── agent.py
│ │ │ │ │ └── proxy.py
│ │ │ │ ├── loops
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── anthropic.py
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── composed_grounded.py
│ │ │ │ │ ├── gelato.py
│ │ │ │ │ ├── gemini.py
│ │ │ │ │ ├── generic_vlm.py
│ │ │ │ │ ├── glm45v.py
│ │ │ │ │ ├── gta1.py
│ │ │ │ │ ├── holo.py
│ │ │ │ │ ├── internvl.py
│ │ │ │ │ ├── model_types.csv
│ │ │ │ │ ├── moondream3.py
│ │ │ │ │ ├── omniparser.py
│ │ │ │ │ ├── openai.py
│ │ │ │ │ ├── opencua.py
│ │ │ │ │ ├── uiins.py
│ │ │ │ │ ├── uitars.py
│ │ │ │ │ └── uitars2.py
│ │ │ │ ├── proxy
│ │ │ │ │ ├── examples.py
│ │ │ │ │ └── handlers.py
│ │ │ │ ├── responses.py
│ │ │ │ ├── tools
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ └── browser_tool.py
│ │ │ │ ├── types.py
│ │ │ │ └── ui
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __main__.py
│ │ │ │ └── gradio
│ │ │ │ ├── __init__.py
│ │ │ │ ├── app.py
│ │ │ │ └── ui_components.py
│ │ │ ├── benchmarks
│ │ │ │ ├── .gitignore
│ │ │ │ ├── contrib.md
│ │ │ │ ├── interactive.py
│ │ │ │ ├── models
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── base.py
│ │ │ │ │ └── gta1.py
│ │ │ │ ├── README.md
│ │ │ │ ├── ss-pro.py
│ │ │ │ ├── ss-v2.py
│ │ │ │ └── utils.py
│ │ │ ├── example.py
│ │ │ ├── pyproject.toml
│ │ │ ├── README.md
│ │ │ └── tests
│ │ │ ├── conftest.py
│ │ │ └── test_computer_agent.py
│ │ ├── bench-ui
│ │ │ ├── bench_ui
│ │ │ │ ├── __init__.py
│ │ │ │ ├── api.py
│ │ │ │ └── child.py
│ │ │ ├── examples
│ │ │ │ ├── folder_example.py
│ │ │ │ ├── gui
│ │ │ │ │ ├── index.html
│ │ │ │ │ ├── logo.svg
│ │ │ │ │ └── styles.css
│ │ │ │ ├── output_overlay.png
│ │ │ │ └── simple_example.py
│ │ │ ├── pyproject.toml
│ │ │ ├── README.md
│ │ │ └── tests
│ │ │ └── test_port_detection.py
│ │ ├── computer
│ │ │ ├── .bumpversion.cfg
│ │ │ ├── computer
│ │ │ │ ├── __init__.py
│ │ │ │ ├── computer.py
│ │ │ │ ├── diorama_computer.py
│ │ │ │ ├── helpers.py
│ │ │ │ ├── interface
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── factory.py
│ │ │ │ │ ├── generic.py
│ │ │ │ │ ├── linux.py
│ │ │ │ │ ├── macos.py
│ │ │ │ │ ├── models.py
│ │ │ │ │ └── windows.py
│ │ │ │ ├── logger.py
│ │ │ │ ├── models.py
│ │ │ │ ├── providers
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── cloud
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ └── provider.py
│ │ │ │ │ ├── docker
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ └── provider.py
│ │ │ │ │ ├── factory.py
│ │ │ │ │ ├── lume
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ └── provider.py
│ │ │ │ │ ├── lume_api.py
│ │ │ │ │ ├── lumier
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ └── provider.py
│ │ │ │ │ ├── types.py
│ │ │ │ │ └── winsandbox
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── provider.py
│ │ │ │ │ └── setup_script.ps1
│ │ │ │ ├── tracing_wrapper.py
│ │ │ │ ├── tracing.py
│ │ │ │ ├── ui
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ └── gradio
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ └── app.py
│ │ │ │ └── utils.py
│ │ │ ├── poetry.toml
│ │ │ ├── pyproject.toml
│ │ │ ├── README.md
│ │ │ └── tests
│ │ │ ├── conftest.py
│ │ │ └── test_computer.py
│ │ ├── computer-server
│ │ │ ├── .bumpversion.cfg
│ │ │ ├── computer_server
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __main__.py
│ │ │ │ ├── browser.py
│ │ │ │ ├── cli.py
│ │ │ │ ├── diorama
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── diorama_computer.py
│ │ │ │ │ ├── diorama.py
│ │ │ │ │ ├── draw.py
│ │ │ │ │ ├── macos.py
│ │ │ │ │ └── safezone.py
│ │ │ │ ├── handlers
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── factory.py
│ │ │ │ │ ├── generic.py
│ │ │ │ │ ├── linux.py
│ │ │ │ │ ├── macos.py
│ │ │ │ │ └── windows.py
│ │ │ │ ├── main.py
│ │ │ │ ├── server.py
│ │ │ │ ├── utils
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ └── wallpaper.py
│ │ │ │ └── watchdog.py
│ │ │ ├── examples
│ │ │ │ ├── __init__.py
│ │ │ │ └── usage_example.py
│ │ │ ├── pyproject.toml
│ │ │ ├── README.md
│ │ │ ├── run_server.py
│ │ │ ├── test_connection.py
│ │ │ └── tests
│ │ │ ├── conftest.py
│ │ │ └── test_server.py
│ │ ├── core
│ │ │ ├── .bumpversion.cfg
│ │ │ ├── core
│ │ │ │ ├── __init__.py
│ │ │ │ └── telemetry
│ │ │ │ ├── __init__.py
│ │ │ │ └── posthog.py
│ │ │ ├── poetry.toml
│ │ │ ├── pyproject.toml
│ │ │ ├── README.md
│ │ │ └── tests
│ │ │ ├── conftest.py
│ │ │ └── test_telemetry.py
│ │ ├── mcp-server
│ │ │ ├── .bumpversion.cfg
│ │ │ ├── build-extension.py
│ │ │ ├── CONCURRENT_SESSIONS.md
│ │ │ ├── desktop-extension
│ │ │ │ ├── cua-extension.mcpb
│ │ │ │ ├── desktop_extension.png
│ │ │ │ ├── manifest.json
│ │ │ │ ├── README.md
│ │ │ │ ├── requirements.txt
│ │ │ │ ├── run_server.sh
│ │ │ │ └── setup.py
│ │ │ ├── mcp_server
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __main__.py
│ │ │ │ ├── server.py
│ │ │ │ └── session_manager.py
│ │ │ ├── pdm.lock
│ │ │ ├── pyproject.toml
│ │ │ ├── QUICK_TEST_COMMANDS.sh
│ │ │ ├── quick_test_local_option.py
│ │ │ ├── README.md
│ │ │ ├── scripts
│ │ │ │ ├── install_mcp_server.sh
│ │ │ │ └── start_mcp_server.sh
│ │ │ ├── test_mcp_server_local_option.py
│ │ │ └── tests
│ │ │ ├── conftest.py
│ │ │ └── test_mcp_server.py
│ │ ├── pylume
│ │ │ └── tests
│ │ │ ├── conftest.py
│ │ │ └── test_pylume.py
│ │ └── som
│ │ ├── .bumpversion.cfg
│ │ ├── LICENSE
│ │ ├── poetry.toml
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── som
│ │ │ ├── __init__.py
│ │ │ ├── detect.py
│ │ │ ├── detection.py
│ │ │ ├── models.py
│ │ │ ├── ocr.py
│ │ │ ├── util
│ │ │ │ └── utils.py
│ │ │ └── visualization.py
│ │ └── tests
│ │ ├── conftest.py
│ │ └── test_omniparser.py
│ ├── qemu-docker
│ │ ├── linux
│ │ │ ├── Dockerfile
│ │ │ ├── README.md
│ │ │ └── src
│ │ │ ├── entry.sh
│ │ │ └── vm
│ │ │ ├── image
│ │ │ │ └── README.md
│ │ │ └── setup
│ │ │ ├── install.sh
│ │ │ ├── setup-cua-server.sh
│ │ │ └── setup.sh
│ │ ├── README.md
│ │ └── windows
│ │ ├── Dockerfile
│ │ ├── README.md
│ │ └── src
│ │ ├── entry.sh
│ │ └── vm
│ │ ├── image
│ │ │ └── README.md
│ │ └── setup
│ │ ├── install.bat
│ │ ├── on-logon.ps1
│ │ ├── setup-cua-server.ps1
│ │ ├── setup-utils.psm1
│ │ └── setup.ps1
│ ├── typescript
│ │ ├── .gitignore
│ │ ├── .nvmrc
│ │ ├── agent
│ │ │ ├── examples
│ │ │ │ ├── playground-example.html
│ │ │ │ └── README.md
│ │ │ ├── package.json
│ │ │ ├── README.md
│ │ │ ├── src
│ │ │ │ ├── client.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── types.ts
│ │ │ ├── tests
│ │ │ │ └── client.test.ts
│ │ │ ├── tsconfig.json
│ │ │ ├── tsdown.config.ts
│ │ │ └── vitest.config.ts
│ │ ├── computer
│ │ │ ├── .editorconfig
│ │ │ ├── .gitattributes
│ │ │ ├── .gitignore
│ │ │ ├── LICENSE
│ │ │ ├── package.json
│ │ │ ├── README.md
│ │ │ ├── src
│ │ │ │ ├── computer
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── providers
│ │ │ │ │ │ ├── base.ts
│ │ │ │ │ │ ├── cloud.ts
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ └── types.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── interface
│ │ │ │ │ ├── base.ts
│ │ │ │ │ ├── factory.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── linux.ts
│ │ │ │ │ ├── macos.ts
│ │ │ │ │ └── windows.ts
│ │ │ │ └── types.ts
│ │ │ ├── tests
│ │ │ │ ├── computer
│ │ │ │ │ └── cloud.test.ts
│ │ │ │ ├── interface
│ │ │ │ │ ├── factory.test.ts
│ │ │ │ │ ├── index.test.ts
│ │ │ │ │ ├── linux.test.ts
│ │ │ │ │ ├── macos.test.ts
│ │ │ │ │ └── windows.test.ts
│ │ │ │ └── setup.ts
│ │ │ ├── tsconfig.json
│ │ │ ├── tsdown.config.ts
│ │ │ └── vitest.config.ts
│ │ ├── core
│ │ │ ├── .editorconfig
│ │ │ ├── .gitattributes
│ │ │ ├── .gitignore
│ │ │ ├── LICENSE
│ │ │ ├── package.json
│ │ │ ├── README.md
│ │ │ ├── src
│ │ │ │ ├── index.ts
│ │ │ │ └── telemetry
│ │ │ │ ├── clients
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── posthog.ts
│ │ │ │ └── index.ts
│ │ │ ├── tests
│ │ │ │ └── telemetry.test.ts
│ │ │ ├── tsconfig.json
│ │ │ ├── tsdown.config.ts
│ │ │ └── vitest.config.ts
│ │ ├── cua-cli
│ │ │ ├── .gitignore
│ │ │ ├── .prettierrc
│ │ │ ├── bun.lock
│ │ │ ├── CLAUDE.md
│ │ │ ├── index.ts
│ │ │ ├── package.json
│ │ │ ├── README.md
│ │ │ ├── src
│ │ │ │ ├── auth.ts
│ │ │ │ ├── cli.ts
│ │ │ │ ├── commands
│ │ │ │ │ ├── auth.ts
│ │ │ │ │ └── sandbox.ts
│ │ │ │ ├── config.ts
│ │ │ │ ├── http.ts
│ │ │ │ ├── storage.ts
│ │ │ │ └── util.ts
│ │ │ └── tsconfig.json
│ │ ├── package.json
│ │ ├── pnpm-lock.yaml
│ │ ├── pnpm-workspace.yaml
│ │ └── README.md
│ └── xfce
│ ├── .dockerignore
│ ├── .gitignore
│ ├── Development.md
│ ├── Dockerfile
│ ├── Dockerfile.dev
│ ├── README.md
│ └── src
│ ├── scripts
│ │ ├── resize-display.sh
│ │ ├── start-computer-server.sh
│ │ ├── start-novnc.sh
│ │ ├── start-vnc.sh
│ │ └── xstartup.sh
│ ├── supervisor
│ │ └── supervisord.conf
│ └── xfce-config
│ ├── helpers.rc
│ ├── xfce4-power-manager.xml
│ └── xfce4-session.xml
├── LICENSE.md
├── Makefile
├── notebooks
│ ├── agent_nb.ipynb
│ ├── blog
│ │ ├── build-your-own-operator-on-macos-1.ipynb
│ │ └── build-your-own-operator-on-macos-2.ipynb
│ ├── composite_agents_docker_nb.ipynb
│ ├── computer_nb.ipynb
│ ├── computer_server_nb.ipynb
│ ├── customizing_computeragent.ipynb
│ ├── eval_osworld.ipynb
│ ├── ollama_nb.ipynb
│ ├── README.md
│ ├── sota_hackathon_cloud.ipynb
│ └── sota_hackathon.ipynb
├── package-lock.json
├── package.json
├── pnpm-lock.yaml
├── pyproject.toml
├── pyrightconfig.json
├── README.md
├── scripts
│ ├── install-cli.ps1
│ ├── install-cli.sh
│ ├── playground-docker.sh
│ ├── playground.sh
│ ├── run-docker-dev.sh
│ └── typescript-typecheck.js
├── TESTING.md
├── tests
│ ├── agent_loop_testing
│ │ ├── agent_test.py
│ │ └── README.md
│ ├── pytest.ini
│ ├── shell_cmd.py
│ ├── test_files.py
│ ├── test_mcp_server_session_management.py
│ ├── test_mcp_server_streaming.py
│ ├── test_shell_bash.py
│ ├── test_telemetry.py
│ ├── test_tracing.py
│ ├── test_venv.py
│ └── test_watchdog.py
└── uv.lock
```
# Files
--------------------------------------------------------------------------------
/libs/python/core/tests/conftest.py:
--------------------------------------------------------------------------------
```python
1 | """Pytest configuration and shared fixtures for core package tests.
2 |
3 | This file contains shared fixtures and configuration for all core tests.
4 | Following SRP: This file ONLY handles test setup/teardown.
5 | """
6 |
7 | from unittest.mock import AsyncMock, Mock, patch
8 |
9 | import pytest
10 |
11 |
12 | @pytest.fixture
13 | def mock_httpx_client():
14 | """Mock httpx.AsyncClient for API calls.
15 |
16 | Use this fixture to avoid making real HTTP requests during tests.
17 | """
18 | with patch("httpx.AsyncClient") as mock_client:
19 | mock_instance = AsyncMock()
20 | mock_client.return_value.__aenter__.return_value = mock_instance
21 | yield mock_instance
22 |
23 |
24 | @pytest.fixture
25 | def mock_posthog():
26 | """Mock PostHog client for telemetry tests.
27 |
28 | Use this fixture to avoid sending real telemetry during tests.
29 | """
30 | with patch("posthog.Posthog") as mock_ph:
31 | mock_instance = Mock()
32 | mock_ph.return_value = mock_instance
33 | yield mock_instance
34 |
35 |
36 | @pytest.fixture
37 | def disable_telemetry(monkeypatch):
38 | """Disable telemetry for tests that don't need it.
39 |
40 | Use this fixture to ensure telemetry is disabled during tests.
41 | """
42 | monkeypatch.setenv("CUA_TELEMETRY_DISABLED", "1")
43 | yield
44 |
```
--------------------------------------------------------------------------------
/docs/src/assets/discord-black.svg:
--------------------------------------------------------------------------------
```
1 | <?xml version="1.0" encoding="UTF-8"?><svg id="Discord-Logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 126.644 96"><path fill="currentColor" id="Discord-Symbol-Black" d="M81.15,0c-1.2376,2.1973-2.3489,4.4704-3.3591,6.794-9.5975-1.4396-19.3718-1.4396-28.9945,0-.985-2.3236-2.1216-4.5967-3.3591-6.794-9.0166,1.5407-17.8059,4.2431-26.1405,8.0568C2.779,32.5304-1.6914,56.3725.5312,79.8863c9.6732,7.1476,20.5083,12.603,32.0505,16.0884,2.6014-3.4854,4.8998-7.1981,6.8698-11.0623-3.738-1.3891-7.3497-3.1318-10.8098-5.1523.9092-.6567,1.7932-1.3386,2.6519-1.9953,20.281,9.547,43.7696,9.547,64.0758,0,.8587.7072,1.7427,1.3891,2.6519,1.9953-3.4601,2.0457-7.0718,3.7632-10.835,5.1776,1.97,3.8642,4.2683,7.5769,6.8698,11.0623,11.5419-3.4854,22.3769-8.9156,32.0509-16.0631,2.626-27.2771-4.496-50.9172-18.817-71.8548C98.9811,4.2684,90.1918,1.5659,81.1752.0505l-.0252-.0505ZM42.2802,65.4144c-6.2383,0-11.4159-5.6575-11.4159-12.6535s4.9755-12.6788,11.3907-12.6788,11.5169,5.708,11.4159,12.6788c-.101,6.9708-5.026,12.6535-11.3907,12.6535ZM84.3576,65.4144c-6.2637,0-11.3907-5.6575-11.3907-12.6535s4.9755-12.6788,11.3907-12.6788,11.4917,5.708,11.3906,12.6788c-.101,6.9708-5.026,12.6535-11.3906,12.6535Z"/></svg>
```
--------------------------------------------------------------------------------
/libs/lume/tests/Mocks/MockVM.swift:
--------------------------------------------------------------------------------
```swift
1 | import Foundation
2 |
3 | @testable import lume
4 |
5 | @MainActor
6 | class MockVM: VM {
7 | private var mockIsRunning = false
8 |
9 | override func getOSType() -> String {
10 | return "mock-os"
11 | }
12 |
13 | override func setup(
14 | ipswPath: String, cpuCount: Int, memorySize: UInt64, diskSize: UInt64, display: String
15 | ) async throws {
16 | // Mock setup implementation
17 | vmDirContext.config.setCpuCount(cpuCount)
18 | vmDirContext.config.setMemorySize(memorySize)
19 | vmDirContext.config.setDiskSize(diskSize)
20 | vmDirContext.config.setMacAddress("00:11:22:33:44:55")
21 | try vmDirContext.saveConfig()
22 | }
23 |
24 | override func run(
25 | noDisplay: Bool, sharedDirectories: [SharedDirectory], mount: Path?, vncPort: Int = 0,
26 | recoveryMode: Bool = false, usbMassStoragePaths: [Path]? = nil
27 | ) async throws {
28 | mockIsRunning = true
29 | try await super.run(
30 | noDisplay: noDisplay, sharedDirectories: sharedDirectories, mount: mount,
31 | vncPort: vncPort, recoveryMode: recoveryMode,
32 | usbMassStoragePaths: usbMassStoragePaths
33 | )
34 | }
35 |
36 | override func stop() async throws {
37 | mockIsRunning = false
38 | try await super.stop()
39 | }
40 | }
41 |
```
--------------------------------------------------------------------------------
/libs/lume/src/Commands/Set.swift:
--------------------------------------------------------------------------------
```swift
1 | import ArgumentParser
2 | import Foundation
3 |
4 | struct Set: AsyncParsableCommand {
5 | static let configuration = CommandConfiguration(
6 | abstract: "Set new values for CPU, memory, and disk size of a virtual machine"
7 | )
8 |
9 | @Argument(help: "Name of the virtual machine", completion: .custom(completeVMName))
10 | var name: String
11 |
12 | @Option(help: "New number of CPU cores")
13 | var cpu: Int?
14 |
15 | @Option(help: "New memory size, e.g., 8192MB or 8GB.", transform: { try parseSize($0) })
16 | var memory: UInt64?
17 |
18 | @Option(help: "New disk size, e.g., 20480MB or 20GB.", transform: { try parseSize($0) })
19 | var diskSize: UInt64?
20 |
21 | @Option(help: "New display resolution in format WIDTHxHEIGHT.")
22 | var display: VMDisplayResolution?
23 |
24 | @Option(name: .customLong("storage"), help: "VM storage location to use or direct path to VM location")
25 | var storage: String?
26 |
27 | init() {
28 | }
29 |
30 | @MainActor
31 | func run() async throws {
32 | let vmController = LumeController()
33 | try vmController.updateSettings(
34 | name: name,
35 | cpu: cpu,
36 | memory: memory,
37 | diskSize: diskSize,
38 | display: display?.string,
39 | storage: storage
40 | )
41 | }
42 | }
43 |
```
--------------------------------------------------------------------------------
/libs/python/bench-ui/examples/gui/index.html:
--------------------------------------------------------------------------------
```html
1 | <!DOCTYPE html>
2 | <html lang="en">
3 |
4 | <head>
5 | <meta charset="UTF-8">
6 | <meta name="viewport" content="width=device-width, initial-scale=1.0">
7 | <title>Static Folder Example</title>
8 | <link rel="stylesheet" href="styles.css">
9 | </head>
10 |
11 | <body>
12 | <div class="container">
13 | <h1>Static Folder Example</h1>
14 | <p>This page is served from a static folder using bench-ui!</p>
15 |
16 | <div class="image-container">
17 | <img src="logo.svg" alt="Example SVG Logo" class="logo">
18 | </div>
19 |
20 | <div class="info">
21 | <p>This example demonstrates:</p>
22 | <ul>
23 | <li>Serving a static folder with bench-ui</li>
24 | <li>Loading external CSS files (styles.css)</li>
25 | <li>Loading SVG images (logo.svg)</li>
26 | </ul>
27 | </div>
28 |
29 | <button id="testButton" class="btn">Click Me!</button>
30 | <p id="status"></p>
31 | </div>
32 |
33 | <script>
34 | document.getElementById('testButton').addEventListener('click', function () {
35 | document.getElementById('status').textContent = 'Button clicked! ✓';
36 | this.disabled = true;
37 | this.textContent = 'Clicked!';
38 | });
39 | </script>
40 | </body>
41 |
42 | </html>
```
--------------------------------------------------------------------------------
/docs/src/providers/posthog-provider.tsx:
--------------------------------------------------------------------------------
```typescript
1 | 'use client';
2 |
3 | import posthog from 'posthog-js';
4 | import { PostHogProvider } from 'posthog-js/react';
5 | import { useEffect } from 'react';
6 | import { usePathname, useSearchParams } from 'next/navigation';
7 |
8 | if (typeof window !== 'undefined') {
9 | const apiKey = process.env.NEXT_PUBLIC_POSTHOG_API_KEY;
10 |
11 | if (apiKey) {
12 | posthog.init(apiKey, {
13 | api_host: '/docs/api/posthog',
14 | ui_host: process.env.NEXT_PUBLIC_POSTHOG_HOST,
15 | person_profiles: 'always',
16 | capture_pageview: false,
17 | capture_pageleave: true,
18 | });
19 | } else {
20 | console.warn('[PostHog] API key not configured. Analytics will be disabled.');
21 | }
22 | }
23 |
24 | export function PHProvider({ children }: { children: React.ReactNode }) {
25 | return <PostHogProvider client={posthog}>{children}</PostHogProvider>;
26 | }
27 |
28 | export function PostHogPageView(): null {
29 | const pathname = usePathname();
30 | const searchParams = useSearchParams();
31 |
32 | useEffect(() => {
33 | if (pathname) {
34 | let url = window.origin + pathname;
35 | if (searchParams && searchParams.toString()) {
36 | url = url + `?${searchParams.toString()}`;
37 | }
38 |
39 | posthog.capture('$pageview', {
40 | $current_url: url,
41 | });
42 | }
43 | }, [pathname, searchParams]);
44 |
45 | return null;
46 | }
47 |
```
--------------------------------------------------------------------------------
/libs/lume/src/ContainerRegistry/ImagesPrinter.swift:
--------------------------------------------------------------------------------
```swift
1 | import Foundation
2 |
3 | struct ImagesPrinter {
4 | private struct Column: Sendable {
5 | let header: String
6 | let width: Int
7 | let getValue: @Sendable (String) -> String
8 | }
9 |
10 | private static let columns: [Column] = [
11 | Column(header: "name", width: 28) { $0.split(separator: ":").first.map(String.init) ?? $0 },
12 | Column(header: "image_id", width: 16) { $0.split(separator: ":").last.map(String.init) ?? "-" }
13 | ]
14 |
15 | static func print(images: [String]) {
16 | if images.isEmpty {
17 | Swift.print("No images found")
18 | return
19 | }
20 |
21 | printHeader()
22 | images.sorted().forEach(printImage)
23 | }
24 |
25 | private static func printHeader() {
26 | let paddedHeaders = columns.map { $0.header.paddedToWidth($0.width) }
27 | Swift.print(paddedHeaders.joined())
28 | }
29 |
30 | private static func printImage(_ image: String) {
31 | let paddedColumns = columns.map { column in
32 | column.getValue(image).paddedToWidth(column.width)
33 | }
34 | Swift.print(paddedColumns.joined())
35 | }
36 | }
37 |
38 | private extension String {
39 | func paddedToWidth(_ width: Int) -> String {
40 | padding(toLength: width, withPad: " ", startingAt: 0)
41 | }
42 | }
```
--------------------------------------------------------------------------------
/libs/python/computer-server/tests/conftest.py:
--------------------------------------------------------------------------------
```python
1 | """Pytest configuration and shared fixtures for computer-server package tests.
2 |
3 | This file contains shared fixtures and configuration for all computer-server tests.
4 | Following SRP: This file ONLY handles test setup/teardown.
5 | """
6 |
7 | from unittest.mock import AsyncMock, Mock, patch
8 |
9 | import pytest
10 |
11 |
12 | @pytest.fixture
13 | def mock_websocket():
14 | """Mock WebSocket connection for testing.
15 |
16 | Use this fixture to test WebSocket logic without real connections.
17 | """
18 | websocket = AsyncMock()
19 | websocket.send = AsyncMock()
20 | websocket.recv = AsyncMock()
21 | websocket.close = AsyncMock()
22 |
23 | return websocket
24 |
25 |
26 | @pytest.fixture
27 | def mock_computer_interface():
28 | """Mock computer interface for server tests.
29 |
30 | Use this fixture to test server logic without real computer operations.
31 | """
32 | interface = AsyncMock()
33 | interface.screenshot = AsyncMock(return_value=b"fake_screenshot")
34 | interface.left_click = AsyncMock()
35 | interface.type = AsyncMock()
36 | interface.key = AsyncMock()
37 |
38 | return interface
39 |
40 |
41 | @pytest.fixture
42 | def disable_telemetry(monkeypatch):
43 | """Disable telemetry for tests.
44 |
45 | Use this fixture to ensure no telemetry is sent during tests.
46 | """
47 | monkeypatch.setenv("CUA_TELEMETRY_DISABLED", "1")
48 |
```
--------------------------------------------------------------------------------
/docs/src/assets/discord-white.svg:
--------------------------------------------------------------------------------
```
1 | <?xml version="1.0" encoding="UTF-8"?><svg id="Discord-Logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 126.644 96"><defs><style>.cls-1{fill:#fff;}</style></defs><path id="Discord-Symbol-White" class="cls-1" d="M81.15,0c-1.2376,2.1973-2.3489,4.4704-3.3591,6.794-9.5975-1.4396-19.3718-1.4396-28.9945,0-.985-2.3236-2.1216-4.5967-3.3591-6.794-9.0166,1.5407-17.8059,4.2431-26.1405,8.0568C2.779,32.5304-1.6914,56.3725.5312,79.8863c9.6732,7.1476,20.5083,12.603,32.0505,16.0884,2.6014-3.4854,4.8998-7.1981,6.8698-11.0623-3.738-1.3891-7.3497-3.1318-10.8098-5.1523.9092-.6567,1.7932-1.3386,2.6519-1.9953,20.281,9.547,43.7696,9.547,64.0758,0,.8587.7072,1.7427,1.3891,2.6519,1.9953-3.4601,2.0457-7.0718,3.7632-10.835,5.1776,1.97,3.8642,4.2683,7.5769,6.8698,11.0623,11.5419-3.4854,22.3769-8.9156,32.0509-16.0631,2.626-27.2771-4.496-50.9172-18.817-71.8548C98.9811,4.2684,90.1918,1.5659,81.1752.0505l-.0252-.0505ZM42.2802,65.4144c-6.2383,0-11.4159-5.6575-11.4159-12.6535s4.9755-12.6788,11.3907-12.6788,11.5169,5.708,11.4159,12.6788c-.101,6.9708-5.026,12.6535-11.3907,12.6535ZM84.3576,65.4144c-6.2637,0-11.3907-5.6575-11.3907-12.6535s4.9755-12.6788,11.3907-12.6788,11.4917,5.708,11.3906,12.6788c-.101,6.9708-5.026,12.6535-11.3906,12.6535Z"/></svg>
```
--------------------------------------------------------------------------------
/libs/python/agent/agent/__init__.py:
--------------------------------------------------------------------------------
```python
1 | """
2 | agent - Decorator-based Computer Use Agent with liteLLM integration
3 | """
4 |
5 | import logging
6 | import sys
7 |
8 | # Import loops to register them
9 | from . import loops
10 | from .agent import ComputerAgent
11 | from .decorators import register_agent
12 | from .types import AgentResponse, Messages
13 |
14 | __all__ = ["register_agent", "ComputerAgent", "Messages", "AgentResponse"]
15 |
16 | __version__ = "0.4.0"
17 |
18 | logger = logging.getLogger(__name__)
19 |
20 | # Initialize telemetry when the package is imported
21 | try:
22 | # Import from core telemetry for basic functions
23 | from core.telemetry import (
24 | is_telemetry_enabled,
25 | record_event,
26 | )
27 |
28 | # Check if telemetry is enabled
29 | if is_telemetry_enabled():
30 | logger.info("Telemetry is enabled")
31 |
32 | # Record package initialization
33 | record_event(
34 | "module_init",
35 | {
36 | "module": "agent",
37 | "version": __version__,
38 | "python_version": sys.version,
39 | },
40 | )
41 |
42 | else:
43 | logger.info("Telemetry is disabled")
44 | except ImportError as e:
45 | # Telemetry not available
46 | logger.warning(f"Telemetry not available: {e}")
47 | except Exception as e:
48 | # Other issues with telemetry
49 | logger.warning(f"Error initializing telemetry: {e}")
50 |
```
--------------------------------------------------------------------------------
/docs/content/docs/macos-vm-cli-playbook/lumier/building-lumier.mdx:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | title: Building Lumier
3 | ---
4 |
5 | If you want to customize the Lumier container or build it from source, you can follow these steps:
6 |
7 | ```bash
8 | # 1. Navigate to the Lumier directory
9 | cd libs/lumier
10 |
11 | # 2. Build the Docker image locally
12 | docker build -t lumier-custom:latest .
13 |
14 | # 3. Run your custom build
15 | docker run -it --rm \
16 | --name lumier-vm \
17 | -p 8006:8006 \
18 | -e VM_NAME=lumier-vm \
19 | -e VERSION=ghcr.io/trycua/macos-sequoia-cua:latest \
20 | -e CPU_CORES=4 \
21 | -e RAM_SIZE=8192 \
22 | lumier-custom:latest
23 | ```
24 |
25 | ### Customization Options
26 |
27 | The Dockerfile provides several customization points:
28 |
29 | 1. **Base image**: The container uses Debian Bullseye Slim as the base. You can modify this if needed.
30 | 2. **Installed packages**: You can add or remove packages in the apt-get install list.
31 | 3. **Hooks**: Check the `/run/hooks/` directory for scripts that run at specific points during VM lifecycle.
32 | 4. **Configuration**: Review `/run/config/constants.sh` for default settings.
33 |
34 | After making your modifications, you can build and push your custom image to your own Docker Hub repository:
35 |
36 | ```bash
37 | # Build with a custom tag
38 | docker build -t yourusername/lumier:custom .
39 |
40 | # Push to Docker Hub (after docker login)
41 | docker push yourusername/lumier:custom
42 | ```
43 |
```
--------------------------------------------------------------------------------
/libs/python/computer-server/tests/test_server.py:
--------------------------------------------------------------------------------
```python
1 | """Unit tests for computer-server package.
2 |
3 | This file tests ONLY basic server functionality.
4 | Following SRP: This file tests server initialization and basic operations.
5 | All external dependencies are mocked.
6 | """
7 |
8 | from unittest.mock import AsyncMock, Mock, patch
9 |
10 | import pytest
11 |
12 |
13 | class TestServerImports:
14 | """Test server module imports (SRP: Only tests imports)."""
15 |
16 | def test_server_module_exists(self):
17 | """Test that server module can be imported."""
18 | try:
19 | import computer_server
20 |
21 | assert computer_server is not None
22 | except ImportError:
23 | pytest.skip("computer_server module not installed")
24 |
25 |
26 | class TestServerInitialization:
27 | """Test server initialization (SRP: Only tests initialization)."""
28 |
29 | @pytest.mark.asyncio
30 | async def test_server_can_be_imported(self):
31 | """Basic smoke test: verify server components can be imported."""
32 | try:
33 | from computer_server import server
34 |
35 | assert server is not None
36 | except ImportError:
37 | pytest.skip("Server module not available")
38 | except Exception as e:
39 | # Some initialization errors are acceptable in unit tests
40 | pytest.skip(f"Server initialization requires specific setup: {e}")
41 |
```
--------------------------------------------------------------------------------
/libs/python/mcp-server/desktop-extension/run_server.sh:
--------------------------------------------------------------------------------
```bash
1 | #!/bin/bash
2 | # Wrapper script to ensure we use the correct Python with dependencies
3 |
4 | # Try different Python paths in order of preference
5 | PYTHON_PATHS=(
6 | "/Users/yellcw/Documents/GitHub/cua/.venv/bin/python"
7 | "/opt/homebrew/bin/python3"
8 | "/usr/local/bin/python3"
9 | "python3"
10 | )
11 |
12 | # Find the first Python that has the required packages
13 | for python_path in "${PYTHON_PATHS[@]}"; do
14 | if command -v "$python_path" >/dev/null 2>&1; then
15 | # Check if it has the required packages
16 | if "$python_path" -c "import mcp, anyio" >/dev/null 2>&1; then
17 | echo "Using Python: $python_path" >&2
18 | exec "$python_path" "$@"
19 | fi
20 | fi
21 | done
22 |
23 | # If no Python with packages found, try to install them
24 | echo "No Python with required packages found. Attempting to install..." >&2
25 | for python_path in "${PYTHON_PATHS[@]}"; do
26 | if command -v "$python_path" >/dev/null 2>&1; then
27 | echo "Installing packages with: $python_path" >&2
28 | if "$python_path" -m pip install mcp anyio cua-agent[all] cua-computer >/dev/null 2>&1; then
29 | echo "Packages installed successfully with: $python_path" >&2
30 | exec "$python_path" "$@"
31 | fi
32 | fi
33 | done
34 |
35 | echo "Failed to find or install Python with required packages" >&2
36 | exit 1
37 |
```
--------------------------------------------------------------------------------
/libs/typescript/agent/package.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "name": "@trycua/agent",
3 | "version": "0.1.0",
4 | "packageManager": "[email protected]",
5 | "description": "TypeScript SDK for CUA agent interaction",
6 | "type": "module",
7 | "license": "MIT",
8 | "homepage": "https://github.com/trycua/cua/tree/main/libs/typescript/agent",
9 | "bugs": {
10 | "url": "https://github.com/trycua/cua/issues"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/trycua/cua.git"
15 | },
16 | "author": "cua",
17 | "files": [
18 | "dist"
19 | ],
20 | "main": "./dist/index.js",
21 | "module": "./dist/index.js",
22 | "types": "./dist/index.d.ts",
23 | "exports": {
24 | ".": "./dist/index.js",
25 | "./package.json": "./package.json"
26 | },
27 | "publishConfig": {
28 | "access": "public"
29 | },
30 | "scripts": {
31 | "lint": "prettier --check .",
32 | "lint:fix": "prettier --write .",
33 | "build": "tsdown",
34 | "dev": "tsdown --watch",
35 | "test": "vitest",
36 | "typecheck": "tsc --noEmit",
37 | "release": "bumpp && pnpm publish",
38 | "prepublishOnly": "pnpm run build"
39 | },
40 | "dependencies": {
41 | "@trycua/core": "^0.1.2",
42 | "peerjs": "^1.5.4",
43 | "pino": "^9.7.0"
44 | },
45 | "devDependencies": {
46 | "@types/node": "^22.15.17",
47 | "bumpp": "^10.1.0",
48 | "happy-dom": "^20.0.11",
49 | "tsdown": "^0.14.1",
50 | "typescript": "^5.7.2",
51 | "vitest": "^4.0.14"
52 | }
53 | }
54 |
```
--------------------------------------------------------------------------------
/libs/typescript/computer/tests/interface/index.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { describe, expect, it } from 'vitest';
2 | import * as InterfaceExports from '../../src/interface/index.ts';
3 |
4 | describe('Interface Module Exports', () => {
5 | it('should export InterfaceFactory', () => {
6 | expect(InterfaceExports.InterfaceFactory).toBeDefined();
7 | expect(InterfaceExports.InterfaceFactory.createInterfaceForOS).toBeDefined();
8 | });
9 |
10 | it('should export BaseComputerInterface', () => {
11 | expect(InterfaceExports.BaseComputerInterface).toBeDefined();
12 | });
13 |
14 | it('should export MacOSComputerInterface', () => {
15 | expect(InterfaceExports.MacOSComputerInterface).toBeDefined();
16 | });
17 |
18 | it('should export LinuxComputerInterface', () => {
19 | expect(InterfaceExports.LinuxComputerInterface).toBeDefined();
20 | });
21 |
22 | it('should export WindowsComputerInterface', () => {
23 | expect(InterfaceExports.WindowsComputerInterface).toBeDefined();
24 | });
25 |
26 | it('should export all expected interfaces', () => {
27 | const expectedExports = [
28 | 'InterfaceFactory',
29 | 'BaseComputerInterface',
30 | 'MacOSComputerInterface',
31 | 'LinuxComputerInterface',
32 | 'WindowsComputerInterface',
33 | ];
34 |
35 | const actualExports = Object.keys(InterfaceExports);
36 | for (const exportName of expectedExports) {
37 | expect(actualExports).toContain(exportName);
38 | }
39 | });
40 | });
41 |
```
--------------------------------------------------------------------------------
/docs/content/docs/cli-playbook/index.mdx:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | title: Getting Started
3 | description: Install and set up the CUA CLI
4 | ---
5 |
6 | import { Tabs, Tab } from 'fumadocs-ui/components/tabs';
7 | import { Callout } from 'fumadocs-ui/components/callout';
8 |
9 | The Cua CLI is a command-line tool for managing your Cua cloud sandboxes. Create, start, stop, and connect to sandboxes directly from your terminal.
10 |
11 | ## Installation
12 |
13 | <Tabs items={['macOS / Linux', 'Windows']}>
14 | <Tab value="macOS / Linux">
15 | ```bash
16 | curl -LsSf https://cua.ai/cli/install.sh | sh
17 | ```
18 | </Tab>
19 | <Tab value="Windows">
20 | ```powershell
21 | powershell -ExecutionPolicy ByPass -c "irm https://cua.ai/cli/install.ps1 | iex"
22 | ```
23 | </Tab>
24 | </Tabs>
25 |
26 | This installs [Bun](https://bun.sh) and the CUA CLI. Verify with:
27 |
28 | ```bash
29 | cua --help
30 | ```
31 |
32 | ## Authentication
33 |
34 | Login to your CUA account:
35 |
36 | ```bash
37 | # Browser-based login
38 | cua auth login
39 |
40 | # Or with API key
41 | cua auth login --api-key sk-your-api-key-here
42 | ```
43 |
44 | Generate a `.env` file for your project:
45 |
46 | ```bash
47 | cua auth env
48 | ```
49 |
50 | ## Quick Start
51 |
52 | ```bash
53 | # Create a sandbox
54 | cua create --os linux --size small --region north-america
55 |
56 | # List sandboxes
57 | cua list
58 |
59 | # Open VNC in browser
60 | cua vnc my-sandbox
61 |
62 | # Stop a sandbox
63 | cua stop my-sandbox
64 | ```
65 |
66 | ## Next Steps
67 |
68 | - [Command Reference](/cli-playbook/commands) - Full list of available commands
69 |
```
--------------------------------------------------------------------------------
/docs/content/docs/macos-vm-cli-playbook/lumier/index.mdx:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | title: Lumier
3 | description: Reference for the current version of the Lumier library.
4 | github:
5 | - https://github.com/trycua/cua/tree/main/libs/lumier
6 | ---
7 |
8 | **Lumier** is an interface for running macOS virtual machines with minimal setup. It uses Docker as a packaging system to deliver a pre-configured environment that connects to the `lume` virtualization service running on your host machine. With Lumier, you get:
9 |
10 | - A ready-to-use macOS or Linux virtual machine in minutes
11 | - Browser-based VNC access to your VM
12 | - Easy file sharing between your host and VM
13 | - Simple configuration through environment variables
14 |
15 | ## How It Works
16 |
17 | <Callout title="Note">
18 | We're using Docker primarily as a convenient delivery mechanism, not as an isolation layer. Unlike
19 | traditional Docker containers, Lumier leverages the Apple Virtualization Framework (Apple Vz)
20 | through the `lume` CLI to create true virtual machines.
21 | </Callout>
22 |
23 | Here's what's happening behind the scenes:
24 |
25 | 1. The Docker container provides a consistent environment to run the Lumier interface
26 | 2. Lumier connects to the Lume service running on your host Mac
27 | 3. Lume uses Apple's Virtualization Framework to create a true macOS virtual machine
28 | 4. The VM runs with hardware acceleration using your Mac's native virtualization capabilities
29 |
```
--------------------------------------------------------------------------------
/libs/typescript/computer/src/interface/factory.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Factory for creating computer interfaces.
3 | */
4 |
5 | import type { OSType } from '../types';
6 | import type { BaseComputerInterface } from './base';
7 | import { LinuxComputerInterface } from './linux';
8 | import { MacOSComputerInterface } from './macos';
9 | import { WindowsComputerInterface } from './windows';
10 |
11 | export const InterfaceFactory = {
12 | /**
13 | * Create an interface for the specified OS.
14 | *
15 | * @param os Operating system type ('macos', 'linux', or 'windows')
16 | * @param ipAddress IP address of the computer to control
17 | * @param apiKey Optional API key for cloud authentication
18 | * @param vmName Optional VM name for cloud authentication
19 | * @returns The appropriate interface for the OS
20 | * @throws Error if the OS type is not supported
21 | */
22 | createInterfaceForOS(
23 | os: OSType,
24 | ipAddress: string,
25 | apiKey?: string,
26 | vmName?: string
27 | ): BaseComputerInterface {
28 | switch (os) {
29 | case 'macos':
30 | return new MacOSComputerInterface(ipAddress, 'lume', 'lume', apiKey, vmName);
31 | case 'linux':
32 | return new LinuxComputerInterface(ipAddress, 'lume', 'lume', apiKey, vmName);
33 | case 'windows':
34 | return new WindowsComputerInterface(ipAddress, 'lume', 'lume', apiKey, vmName);
35 | default:
36 | throw new Error(`Unsupported OS type: ${os}`);
37 | }
38 | },
39 | };
40 |
```
--------------------------------------------------------------------------------
/docs/src/components/hero.tsx:
--------------------------------------------------------------------------------
```typescript
1 | export function Hero({ children }: { children: React.ReactNode }) {
2 | return (
3 | <div className="not-prose relative mb-12 overflow-hidden rounded-xl border border-fd-border bg-gradient-to-br from-fd-background via-fd-muted/30 to-fd-muted/50 p-8 shadow-lg md:p-12 lg:p-16">
4 | {/* Background Pattern */}
5 | <div className="pointer-events-none absolute inset-0">
6 | {/* Grid */}
7 | <svg
8 | className="absolute h-full w-full text-fd-foreground"
9 | xmlns="http://www.w3.org/2000/svg"
10 | >
11 | <defs>
12 | <pattern id="hero-grid" width="40" height="40" patternUnits="userSpaceOnUse">
13 | <path
14 | d="M 40 0 L 0 0 0 40"
15 | fill="none"
16 | stroke="currentColor"
17 | strokeWidth="0.5"
18 | opacity="0.1"
19 | />
20 | </pattern>
21 | </defs>
22 | <rect width="100%" height="100%" fill="url(#hero-grid)" />
23 | </svg>
24 |
25 | {/* Subtle glow effects */}
26 | <div className="absolute -right-20 -top-20 h-96 w-96 rounded-full bg-fd-primary/5 blur-3xl" />
27 | <div className="absolute -bottom-32 -left-20 h-96 w-96 rounded-full bg-fd-primary/5 blur-3xl" />
28 | </div>
29 |
30 | {/* Content */}
31 | <div className="relative z-10">{children}</div>
32 | </div>
33 | );
34 | }
35 |
```
--------------------------------------------------------------------------------
/libs/lume/Package.swift:
--------------------------------------------------------------------------------
```swift
1 | // swift-tools-version: 6.0
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "lume",
8 | platforms: [
9 | .macOS(.v14)
10 | ],
11 | dependencies: [
12 | .package(url: "https://github.com/apple/swift-argument-parser", from: "1.3.1"),
13 | .package(url: "https://github.com/apple/swift-format.git", branch: ("release/5.10")),
14 | .package(url: "https://github.com/apple/swift-atomics.git", .upToNextMajor(from: "1.2.0")),
15 | .package(url: "https://github.com/mhdhejazi/Dynamic", branch: "master")
16 | ],
17 | targets: [
18 | // Targets are the basic building blocks of a package, defining a module or a test suite.
19 | // Targets can depend on other targets in this package and products from dependencies.
20 | .executableTarget(
21 | name: "lume",
22 | dependencies: [
23 | .product(name: "ArgumentParser", package: "swift-argument-parser"),
24 | .product(name: "Atomics", package: "swift-atomics"),
25 | .product(name: "Dynamic", package: "Dynamic")
26 | ],
27 | path: "src"),
28 | .testTarget(
29 | name: "lumeTests",
30 | dependencies: [
31 | "lume"
32 | ],
33 | path: "tests")
34 | ]
35 | )
36 |
```
--------------------------------------------------------------------------------
/docs/src/components/cookie-consent.tsx:
--------------------------------------------------------------------------------
```typescript
1 | 'use client';
2 |
3 | import { useEffect, useState } from 'react';
4 | import posthog from 'posthog-js';
5 |
6 | export function CookieConsent() {
7 | const [isVisible, setIsVisible] = useState(false);
8 |
9 | useEffect(() => {
10 | // Check if user has already accepted cookies
11 | const hasAccepted = localStorage.getItem('cookie-consent');
12 | if (!hasAccepted) {
13 | setIsVisible(true);
14 | }
15 | }, []);
16 |
17 | const handleAccept = () => {
18 | localStorage.setItem('cookie-consent', 'accepted');
19 | setIsVisible(false);
20 |
21 | // Track cookie acceptance
22 | posthog.capture('cookie_consent_accepted', {
23 | page: window.location.pathname,
24 | });
25 | };
26 |
27 | if (!isVisible) return null;
28 |
29 | return (
30 | <div className="fixed bottom-0 left-0 right-0 z-50 bg-fd-background border-t border-fd-border shadow-lg">
31 | <div className="container mx-auto px-4 py-2 flex flex-col sm:flex-row items-center justify-between gap-3">
32 | <p className="text-xs text-fd-muted-foreground">
33 | This site uses cookies for website functionality, analytics, and personalized content.
34 | </p>
35 | <button
36 | onClick={handleAccept}
37 | className="px-4 py-1 text-xs bg-fd-primary text-fd-primary-foreground rounded hover:opacity-90 transition-opacity whitespace-nowrap"
38 | >
39 | Okay
40 | </button>
41 | </div>
42 | </div>
43 | );
44 | }
45 |
```
--------------------------------------------------------------------------------
/docs/src/components/mermaid.tsx:
--------------------------------------------------------------------------------
```typescript
1 | 'use client';
2 |
3 | import { useEffect, useId, useRef, useState } from 'react';
4 | import { useTheme } from 'next-themes';
5 |
6 | export function Mermaid({ chart }: { chart: string }) {
7 | const id = useId();
8 | const [svg, setSvg] = useState('');
9 | const containerRef = useRef<HTMLDivElement>(null);
10 | const currentChartRef = useRef<string>(null);
11 | const { resolvedTheme } = useTheme();
12 |
13 | useEffect(() => {
14 | if (currentChartRef.current === chart || !containerRef.current) return;
15 | const container = containerRef.current;
16 | currentChartRef.current = chart;
17 |
18 | async function renderChart() {
19 | const { default: mermaid } = await import('mermaid');
20 |
21 | try {
22 | // configure mermaid
23 | mermaid.initialize({
24 | startOnLoad: false,
25 | securityLevel: 'loose',
26 | fontFamily: 'inherit',
27 | themeCSS: 'margin: 1.5rem auto 0;',
28 | theme: resolvedTheme === 'dark' ? 'dark' : 'default',
29 | });
30 |
31 | const { svg, bindFunctions } = await mermaid.render(id, chart.replaceAll('\\n', '\n'));
32 |
33 | bindFunctions?.(container);
34 | setSvg(svg);
35 | } catch (error) {
36 | console.error('Error while rendering mermaid', error);
37 | }
38 | }
39 |
40 | void renderChart();
41 | }, [chart, id, resolvedTheme]);
42 |
43 | return <div ref={containerRef} dangerouslySetInnerHTML={{ __html: svg }} />;
44 | }
45 |
```
--------------------------------------------------------------------------------
/docs/src/app/layout.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import './global.css';
2 | import { RootProvider } from 'fumadocs-ui/provider';
3 | import { Geist, Geist_Mono } from 'next/font/google';
4 | import type { ReactNode } from 'react';
5 | import { PHProvider, PostHogPageView } from '@/providers/posthog-provider';
6 | import { AnalyticsTracker } from '@/components/analytics-tracker';
7 | import { CookieConsent } from '@/components/cookie-consent';
8 | import { Footer } from '@/components/footer';
9 | import { Suspense } from 'react';
10 |
11 | const geist = Geist({
12 | subsets: ['latin'],
13 | variable: '--font-geist-sans',
14 | });
15 |
16 | const geistMono = Geist_Mono({
17 | subsets: ['latin'],
18 | variable: '--font-geist-mono',
19 | });
20 |
21 | export default function Layout({ children }: { children: ReactNode }) {
22 | return (
23 | <html
24 | lang="en"
25 | className={`${geist.variable} ${geistMono.variable} font-sans`}
26 | suppressHydrationWarning
27 | >
28 | <head>
29 | <link rel="icon" href="/docs/favicon.ico" sizes="any" />
30 | </head>
31 | <body className="flex min-h-screen flex-col">
32 | <PHProvider>
33 | <Suspense fallback={null}>
34 | <PostHogPageView />
35 | </Suspense>
36 | <AnalyticsTracker />
37 | <RootProvider search={{ options: { api: '/docs/api/search' } }}>{children}</RootProvider>
38 | <Footer />
39 | <CookieConsent />
40 | </PHProvider>
41 | </body>
42 | </html>
43 | );
44 | }
45 |
```
--------------------------------------------------------------------------------
/.github/workflows/npm-publish-core.yml:
--------------------------------------------------------------------------------
```yaml
1 | name: Publish @trycua/core to npm
2 |
3 | on:
4 | push:
5 | branches: main
6 |
7 | jobs:
8 | publish:
9 | permissions:
10 | id-token: write
11 | contents: read
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v4
15 |
16 | - name: Use Node.js 24.x
17 | uses: actions/setup-node@v4
18 | with:
19 | node-version: "24.x"
20 | registry-url: "https://registry.npmjs.org"
21 |
22 | - name: Setup pnpm 10
23 | uses: pnpm/action-setup@v4
24 | with:
25 | version: 10
26 |
27 | - name: Check if version changed
28 | id: check-version
29 | uses: EndBug/version-check@v2
30 | with:
31 | file-name: libs/typescript/core/package.json
32 | diff-search: true
33 |
34 | - name: Install dependencies
35 | if: steps.check-version.outputs.changed == 'true'
36 | working-directory: ./libs/typescript/core
37 | run: pnpm install --frozen-lockfile
38 |
39 | - name: Build package
40 | if: steps.check-version.outputs.changed == 'true'
41 | working-directory: ./libs/typescript/core
42 | run: pnpm run build --if-present
43 |
44 | - name: Publish to npm
45 | if: steps.check-version.outputs.changed == 'true'
46 | working-directory: ./libs/typescript/core
47 | run: pnpm publish --access public --no-git-checks
48 | env:
49 | NPM_CONFIG_PROVENANCE: true
50 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
51 |
```
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
```yaml
1 | name: Lint & Format Check
2 |
3 | on:
4 | pull_request:
5 |
6 | push:
7 | branches:
8 | - main
9 |
10 | jobs:
11 | lint:
12 | name: Lint & Format
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - name: Checkout repository
17 | uses: actions/checkout@v3
18 |
19 | - name: Set up Node.js
20 | uses: actions/setup-node@v3
21 | with:
22 | node-version: 20
23 |
24 | - name: Set up pnpm
25 | uses: pnpm/action-setup@v4
26 | with:
27 | version: 10
28 |
29 | - name: Set up Python
30 | uses: actions/setup-python@v4
31 | with:
32 | python-version: 3.12
33 |
34 | - name: Install Python dependencies
35 | run: |
36 | pip install uv
37 | uv sync
38 |
39 | - name: Install Node dependencies
40 | run: |
41 | pnpm install --frozen-lockfile
42 | pnpm -C libs/typescript install --frozen-lockfile
43 |
44 | # Python checks (isort, black, ruff, mypy)
45 | - name: Python lint & typecheck
46 | run: |
47 | uv run isort --check-only .
48 | uv run black --check .
49 | uv run ruff check .
50 | # Temporarily disabled due to untyped codebase
51 | # uv run mypy .
52 |
53 | # TypeScript type check
54 | - name: TypeScript typecheck
55 | run: node ./scripts/typescript-typecheck.js
56 |
57 | # JS/TS/Markdown/YAML checks
58 | - name: Prettier check
59 | run: pnpm prettier --check "**/*.{ts,tsx,js,jsx,json,md,yaml,yml}"
60 |
```
--------------------------------------------------------------------------------
/libs/typescript/computer/package.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "name": "@trycua/computer",
3 | "version": "0.1.4",
4 | "packageManager": "[email protected]",
5 | "description": "Typescript SDK for Cua computer interaction",
6 | "type": "module",
7 | "license": "MIT",
8 | "homepage": "https://github.com/trycua/cua/tree/feature/computer/typescript/libs/typescript/computer",
9 | "bugs": {
10 | "url": "https://github.com/trycua/cua/issues"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/trycua/cua.git"
15 | },
16 | "author": "cua",
17 | "files": [
18 | "dist"
19 | ],
20 | "main": "./dist/index.js",
21 | "module": "./dist/index.js",
22 | "types": "./dist/index.d.ts",
23 | "exports": {
24 | ".": "./dist/index.js",
25 | "./package.json": "./package.json"
26 | },
27 | "publishConfig": {
28 | "access": "public"
29 | },
30 | "scripts": {
31 | "lint": "prettier --check .",
32 | "lint:fix": "prettier --write .",
33 | "build": "tsdown",
34 | "dev": "tsdown --watch",
35 | "test": "vitest",
36 | "typecheck": "tsc --noEmit",
37 | "release": "bumpp && pnpm publish",
38 | "prepublishOnly": "pnpm run build"
39 | },
40 | "dependencies": {
41 | "@trycua/core": "^0.1.2",
42 | "pino": "^9.7.0",
43 | "ws": "^8.18.0"
44 | },
45 | "devDependencies": {
46 | "@types/node": "^22.15.17",
47 | "@types/ws": "^8.18.1",
48 | "bumpp": "^10.1.0",
49 | "happy-dom": "^20.0.11",
50 | "tsdown": "^0.11.9",
51 | "tsx": "^4.19.4",
52 | "typescript": "^5.8.3",
53 | "vitest": "^4.0.14"
54 | }
55 | }
56 |
```
--------------------------------------------------------------------------------
/libs/python/bench-ui/tests/test_port_detection.py:
--------------------------------------------------------------------------------
```python
1 | import time
2 |
3 | import psutil
4 | import pytest
5 | from bench_ui import execute_javascript, launch_window
6 | from bench_ui.api import _pid_to_port
7 |
8 | HTML = """
9 | <!doctype html>
10 | <html>
11 | <head>
12 | <meta charset="utf-8" />
13 | <title>Bench UI Test</title>
14 | </head>
15 | <body>
16 | <div id="t">hello-world</div>
17 | </body>
18 | </html>
19 | """
20 |
21 |
22 | def test_execute_js_after_clearing_port_mapping():
23 | # Skip if pywebview backend is unavailable on this machine
24 | pywebview = pytest.importorskip("webview")
25 |
26 | pid = launch_window(html=HTML, title="Bench UI Test", width=400, height=300)
27 | try:
28 | # Give a brief moment for window to render and server to start
29 | time.sleep(1.0)
30 |
31 | # Sanity: mapping should exist initially
32 | assert pid in _pid_to_port
33 |
34 | # Clear the cached mapping to simulate a fresh process lookup
35 | del _pid_to_port[pid]
36 |
37 | # Now execute JS; this should succeed by detecting the port via psutil
38 | result = execute_javascript(pid, "document.querySelector('#t')?.textContent")
39 | assert result == "hello-world"
40 | finally:
41 | # Best-effort cleanup of the child process
42 | try:
43 | p = psutil.Process(pid)
44 | p.terminate()
45 | try:
46 | p.wait(timeout=3)
47 | except psutil.TimeoutExpired:
48 | p.kill()
49 | except Exception:
50 | pass
51 |
```
--------------------------------------------------------------------------------
/libs/typescript/core/tests/telemetry.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { beforeEach, describe, expect, it } from 'vitest';
2 | import { Telemetry } from '../src/';
3 |
4 | describe('Telemetry', () => {
5 | let telemetry: Telemetry;
6 | beforeEach(() => {
7 | process.env.CUA_TELEMETRY_ENABLED = '';
8 | telemetry = new Telemetry();
9 | });
10 | describe('telemetry.enabled', () => {
11 | it('should return false when CUA_TELEMETRY_ENABLED is false', () => {
12 | process.env.CUA_TELEMETRY_ENABLED = 'false';
13 | telemetry = new Telemetry();
14 | expect(telemetry.enabled).toBe(false);
15 | });
16 |
17 | it('should return false when CUA_TELEMETRY_ENABLED is 0', () => {
18 | process.env.CUA_TELEMETRY_ENABLED = '0';
19 | telemetry = new Telemetry();
20 | expect(telemetry.enabled).toBe(false);
21 | });
22 |
23 | it('should return true when CUA_TELEMETRY_ENABLED is not set (default)', () => {
24 | delete process.env.CUA_TELEMETRY_ENABLED;
25 | telemetry = new Telemetry();
26 | expect(telemetry.enabled).toBe(true);
27 | });
28 |
29 | it('should return true when CUA_TELEMETRY_ENABLED is true', () => {
30 | process.env.CUA_TELEMETRY_ENABLED = 'true';
31 | telemetry = new Telemetry();
32 | expect(telemetry.enabled).toBe(true);
33 | });
34 |
35 | it('should return true when CUA_TELEMETRY_ENABLED is 1', () => {
36 | process.env.CUA_TELEMETRY_ENABLED = '1';
37 | telemetry = new Telemetry();
38 | expect(telemetry.enabled).toBe(true);
39 | });
40 | });
41 | });
42 |
```
--------------------------------------------------------------------------------
/libs/typescript/core/package.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "name": "@trycua/core",
3 | "version": "0.1.3",
4 | "packageManager": "[email protected]",
5 | "description": "Typescript SDK for Cua core.",
6 | "type": "module",
7 | "license": "MIT",
8 | "homepage": "https://github.com/trycua/cua/tree/feature/computer/typescript/libs/typescript/computer",
9 | "bugs": {
10 | "url": "https://github.com/trycua/cua/issues"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/trycua/cua.git"
15 | },
16 | "author": "cua",
17 | "files": [
18 | "dist"
19 | ],
20 | "main": "./dist/index.js",
21 | "module": "./dist/index.js",
22 | "types": "./dist/index.d.ts",
23 | "exports": {
24 | ".": "./dist/index.js",
25 | "./package.json": "./package.json"
26 | },
27 | "publishConfig": {
28 | "access": "public"
29 | },
30 | "scripts": {
31 | "lint": "prettier --check .",
32 | "lint:fix": "prettier --write .",
33 | "build": "tsdown",
34 | "dev": "tsdown --watch",
35 | "test": "vitest",
36 | "typecheck": "tsc --noEmit",
37 | "release": "bumpp && pnpm publish",
38 | "prepublishOnly": "pnpm run build"
39 | },
40 | "dependencies": {
41 | "@types/uuid": "^10.0.0",
42 | "pino": "^9.7.0",
43 | "posthog-node": "^5.14.1",
44 | "uuid": "^11.1.0"
45 | },
46 | "devDependencies": {
47 | "@types/node": "^22.15.17",
48 | "@types/ws": "^8.18.1",
49 | "bumpp": "^10.1.0",
50 | "happy-dom": "^20.0.11",
51 | "tsdown": "^0.11.9",
52 | "tsx": "^4.19.4",
53 | "typescript": "^5.8.3",
54 | "vitest": "^4.0.14"
55 | }
56 | }
57 |
```
--------------------------------------------------------------------------------
/.github/workflows/npm-publish-computer.yml:
--------------------------------------------------------------------------------
```yaml
1 | name: Publish @trycua/computer to npm
2 |
3 | on:
4 | push:
5 | branches: main
6 |
7 | jobs:
8 | publish:
9 | permissions:
10 | id-token: write
11 | contents: read
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v4
15 |
16 | - name: Use Node.js 24.x
17 | uses: actions/setup-node@v4
18 | with:
19 | node-version: "24.x"
20 | registry-url: "https://registry.npmjs.org"
21 |
22 | - name: Setup pnpm 10
23 | uses: pnpm/action-setup@v4
24 | with:
25 | version: 10
26 |
27 | - name: Check if version changed
28 | id: check-version
29 | uses: EndBug/version-check@v2
30 | with:
31 | file-name: libs/typescript/computer/package.json
32 | diff-search: true
33 |
34 | - name: Install dependencies
35 | if: steps.check-version.outputs.changed == 'true'
36 | working-directory: ./libs/typescript/computer
37 | run: pnpm install --frozen-lockfile
38 |
39 | - name: Build package
40 | if: steps.check-version.outputs.changed == 'true'
41 | working-directory: ./libs/typescript/computer
42 | run: pnpm run build --if-present
43 |
44 | - name: Publish to npm
45 | if: steps.check-version.outputs.changed == 'true'
46 | working-directory: ./libs/typescript/computer
47 | run: pnpm publish --access public --no-git-checks
48 | env:
49 | NPM_CONFIG_PROVENANCE: true
50 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
51 |
```
--------------------------------------------------------------------------------
/libs/python/mcp-server/tests/conftest.py:
--------------------------------------------------------------------------------
```python
1 | """Pytest configuration and shared fixtures for mcp-server package tests.
2 |
3 | This file contains shared fixtures and configuration for all mcp-server tests.
4 | Following SRP: This file ONLY handles test setup/teardown.
5 | """
6 |
7 | from unittest.mock import AsyncMock, Mock, patch
8 |
9 | import pytest
10 |
11 |
12 | @pytest.fixture
13 | def mock_mcp_context():
14 | """Mock MCP context for testing.
15 |
16 | Use this fixture to test MCP server logic without real MCP connections.
17 | """
18 | context = AsyncMock()
19 | context.request_context = AsyncMock()
20 | context.session = Mock()
21 | context.session.send_resource_updated = AsyncMock()
22 |
23 | return context
24 |
25 |
26 | @pytest.fixture
27 | def mock_computer():
28 | """Mock Computer instance for MCP server tests.
29 |
30 | Use this fixture to test MCP logic without real Computer operations.
31 | """
32 | computer = AsyncMock()
33 | computer.interface = AsyncMock()
34 | computer.interface.screenshot = AsyncMock(return_value=b"fake_screenshot")
35 | computer.interface.left_click = AsyncMock()
36 | computer.interface.type = AsyncMock()
37 |
38 | # Mock context manager
39 | computer.__aenter__ = AsyncMock(return_value=computer)
40 | computer.__aexit__ = AsyncMock()
41 |
42 | return computer
43 |
44 |
45 | @pytest.fixture
46 | def disable_telemetry(monkeypatch):
47 | """Disable telemetry for tests.
48 |
49 | Use this fixture to ensure no telemetry is sent during tests.
50 | """
51 | monkeypatch.setenv("CUA_TELEMETRY_DISABLED", "1")
52 |
```
--------------------------------------------------------------------------------
/docs/content/docs/agent-sdk/callbacks/trajectories.mdx:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | title: Trajectories
3 | description: Recording and viewing agent conversation trajectories
4 | ---
5 |
6 | # Trajectory Saving Callback
7 |
8 | The TrajectorySaverCallback records complete agent conversations including messages, actions, and screenshots for debugging and analysis.
9 |
10 | ## Callbacks Example
11 |
12 | ```python
13 | from agent.callbacks import TrajectorySaverCallback
14 |
15 | agent = ComputerAgent(
16 | model="anthropic/claude-sonnet-4-5-20250929",
17 | tools=[computer],
18 | callbacks=[
19 | TrajectorySaverCallback(
20 | trajectory_dir="my_trajectories",
21 | save_screenshots=True
22 | )
23 | ]
24 | )
25 | ```
26 |
27 | ## Shorthand
28 |
29 | ```python
30 | agent = ComputerAgent(
31 | model="anthropic/claude-sonnet-4-5-20250929",
32 | trajectory_dir="trajectories", # Auto-save trajectories
33 | tools=[computer]
34 | )
35 | ```
36 |
37 | ## View Trajectories Online
38 |
39 | View trajectories in the browser at:
40 | **[cua.ai/trajectory-viewer](https://cua.ai/trajectory-viewer)**
41 |
42 | The viewer provides:
43 |
44 | - Interactive conversation replay
45 | - Screenshot galleries
46 | - No data collection
47 |
48 | ## Trajectory Structure
49 |
50 | Trajectories are saved with:
51 |
52 | - Complete conversation history
53 | - Usage statistics and costs
54 | - Timestamps and metadata
55 | - Screenshots and computer actions
56 |
57 | Each trajectory contains:
58 |
59 | - **metadata.json**: Run info, timestamps, usage stats (`total_tokens`, `response_cost`)
60 | - **turn_000/**: Turn-by-turn conversation history (api calls, responses, computer calls, screenshots)
61 |
```
--------------------------------------------------------------------------------
/examples/utils.py:
--------------------------------------------------------------------------------
```python
1 | """Utility functions for example scripts."""
2 |
3 | import os
4 | import signal
5 | import sys
6 | from pathlib import Path
7 | from typing import Optional
8 |
9 |
10 | def load_env_file(path: Path) -> bool:
11 | """Load environment variables from a file.
12 |
13 | Args:
14 | path: Path to the .env file
15 |
16 | Returns:
17 | True if file was loaded successfully, False otherwise
18 | """
19 | if not path.exists():
20 | return False
21 |
22 | print(f"Loading environment from {path}")
23 | with open(path, "r") as f:
24 | for line in f:
25 | line = line.strip()
26 | if not line or line.startswith("#"):
27 | continue
28 |
29 | key, value = line.split("=", 1)
30 | os.environ[key] = value
31 |
32 | return True
33 |
34 |
35 | def load_dotenv_files():
36 | """Load environment variables from .env files.
37 |
38 | Tries to load from .env.local first, then .env if .env.local doesn't exist.
39 | """
40 | # Get the project root directory (parent of the examples directory)
41 | project_root = Path(__file__).parent.parent
42 |
43 | # Try loading .env.local first, then .env if .env.local doesn't exist
44 | env_local_path = project_root / ".env.local"
45 | env_path = project_root / ".env"
46 |
47 | # Load .env.local if it exists, otherwise try .env
48 | if not load_env_file(env_local_path):
49 | load_env_file(env_path)
50 |
51 |
52 | def handle_sigint(signum, frame):
53 | """Handle SIGINT (Ctrl+C) gracefully."""
54 | print("\nExiting gracefully...")
55 | sys.exit(0)
56 |
```
--------------------------------------------------------------------------------
/libs/lume/Development.md:
--------------------------------------------------------------------------------
```markdown
1 | # Development Guide
2 |
3 | This guide will help you set up your development environment and understand the process for contributing code to lume.
4 |
5 | ## Environment Setup
6 |
7 | Lume development requires:
8 |
9 | - Swift 6 or higher
10 | - Xcode 15 or higher
11 | - macOS Sequoia 15.2 or higher
12 | - (Optional) VS Code with Swift extension
13 |
14 | If you're working on Lume in the context of the Cua monorepo, we recommend using the dedicated VS Code workspace configuration:
15 |
16 | ```bash
17 | # Open VS Code workspace from the root of the monorepo
18 | code .vscode/lume.code-workspace
19 | ```
20 |
21 | This workspace is preconfigured with Swift language support, build tasks, and debug configurations.
22 |
23 | ## Setting Up the Repository Locally
24 |
25 | 1. **Fork the Repository**: Create your own fork of lume
26 | 2. **Clone the Repository**:
27 | ```bash
28 | git clone https://github.com/trycua/lume.git
29 | cd lume
30 | ```
31 | 3. **Install Dependencies**:
32 | ```bash
33 | swift package resolve
34 | ```
35 | 4. **Build the Project**:
36 | ```bash
37 | swift build
38 | ```
39 |
40 | ## Development Workflow
41 |
42 | 1. Create a new branch for your changes
43 | 2. Make your changes
44 | 3. Run the tests: `swift test`
45 | 4. Build and test your changes locally
46 | 5. Commit your changes with clear commit messages
47 |
48 | ## Submitting Pull Requests
49 |
50 | 1. Push your changes to your fork
51 | 2. Open a Pull Request with:
52 | - A clear title and description
53 | - Reference to any related issues
54 | - Screenshots or logs if relevant
55 | 3. Respond to any feedback from maintainers
56 |
```
--------------------------------------------------------------------------------
/libs/lume/src/Commands/Serve.swift:
--------------------------------------------------------------------------------
```swift
1 | import ArgumentParser
2 | import Foundation
3 |
4 | struct Serve: AsyncParsableCommand {
5 | static let configuration = CommandConfiguration(
6 | abstract: "Start the VM management server"
7 | )
8 |
9 | @Option(help: "Port to listen on")
10 | var port: UInt16 = 7777
11 |
12 | func run() async throws {
13 | let server = await Server(port: port)
14 |
15 | Logger.info("Starting server", metadata: ["port": "\(port)"])
16 |
17 | // Using custom error handling to prevent ArgumentParser from printing additional error messages
18 | do {
19 | try await server.start()
20 | } catch let error as PortError {
21 | // For port errors, just log once with the suggestion
22 | let suggestedPort = port + 1
23 |
24 | // Create a user-friendly error message that includes the suggestion
25 | let message = """
26 | \(error.localizedDescription)
27 | Try using a different port: lume serve --port \(suggestedPort)
28 | """
29 |
30 | // Log the message (without the "ERROR:" prefix that ArgumentParser will add)
31 | Logger.error(message)
32 |
33 | // Exit with a custom code to prevent ArgumentParser from printing the error again
34 | Foundation.exit(1)
35 | } catch {
36 | // For other errors, log once
37 | Logger.error("Failed to start server", metadata: ["error": error.localizedDescription])
38 | throw error
39 | }
40 | }
41 | }
```
--------------------------------------------------------------------------------
/libs/python/agent/agent/adapters/models/__init__.py:
--------------------------------------------------------------------------------
```python
1 | from typing import Optional
2 |
3 | try:
4 | from transformers import AutoConfig
5 |
6 | HF_AVAILABLE = True
7 | except ImportError:
8 | HF_AVAILABLE = False
9 |
10 | from .generic import GenericHFModel
11 | from .internvl import InternVLModel
12 | from .opencua import OpenCUAModel
13 | from .qwen2_5_vl import Qwen2_5_VLModel
14 |
15 |
16 | def load_model(model_name: str, device: str = "auto", trust_remote_code: bool = False):
17 | """Factory function to load and return the right model handler instance.
18 |
19 | - If the underlying transformers config class matches OpenCUA, return OpenCUAModel
20 | - Otherwise, return GenericHFModel
21 | """
22 | if not HF_AVAILABLE:
23 | raise ImportError(
24 | 'HuggingFace transformers dependencies not found. Install with: pip install "cua-agent[uitars-hf]"'
25 | )
26 | cfg = AutoConfig.from_pretrained(model_name, trust_remote_code=trust_remote_code)
27 | cls = cfg.__class__.__name__
28 | print(f"cls: {cls}")
29 | if "OpenCUA" in cls:
30 | return OpenCUAModel(
31 | model_name=model_name, device=device, trust_remote_code=trust_remote_code
32 | )
33 | elif "Qwen2_5_VL" in cls:
34 | return Qwen2_5_VLModel(
35 | model_name=model_name, device=device, trust_remote_code=trust_remote_code
36 | )
37 | elif "InternVL" in cls:
38 | return InternVLModel(
39 | model_name=model_name, device=device, trust_remote_code=trust_remote_code
40 | )
41 | return GenericHFModel(model_name=model_name, device=device, trust_remote_code=trust_remote_code)
42 |
```
--------------------------------------------------------------------------------
/libs/python/agent/agent/computers/__init__.py:
--------------------------------------------------------------------------------
```python
1 | """
2 | Computer handler factory and interface definitions.
3 |
4 | This module provides a factory function to create computer handlers from different
5 | computer interface types, supporting both the ComputerHandler protocol and the
6 | Computer library interface.
7 | """
8 |
9 | from computer import Computer as cuaComputer
10 |
11 | from .base import AsyncComputerHandler
12 | from .cua import cuaComputerHandler
13 | from .custom import CustomComputerHandler
14 |
15 |
16 | def is_agent_computer(computer):
17 | """Check if the given computer is a ComputerHandler or CUA Computer."""
18 | return (
19 | isinstance(computer, AsyncComputerHandler)
20 | or isinstance(computer, cuaComputer)
21 | or (isinstance(computer, dict))
22 | ) # and "screenshot" in computer)
23 |
24 |
25 | async def make_computer_handler(computer):
26 | """
27 | Create a computer handler from a computer interface.
28 |
29 | Args:
30 | computer: Either a ComputerHandler instance, Computer instance, or dict of functions
31 |
32 | Returns:
33 | ComputerHandler: A computer handler instance
34 |
35 | Raises:
36 | ValueError: If the computer type is not supported
37 | """
38 | if isinstance(computer, AsyncComputerHandler):
39 | return computer
40 | if isinstance(computer, cuaComputer):
41 | computer_handler = cuaComputerHandler(computer)
42 | await computer_handler._initialize()
43 | return computer_handler
44 | if isinstance(computer, dict):
45 | return CustomComputerHandler(computer)
46 | raise ValueError(f"Unsupported computer type: {type(computer)}")
47 |
```
--------------------------------------------------------------------------------
/libs/python/mcp-server/tests/test_mcp_server.py:
--------------------------------------------------------------------------------
```python
1 | """Unit tests for mcp-server package.
2 |
3 | This file tests ONLY basic MCP server functionality.
4 | Following SRP: This file tests MCP server initialization.
5 | All external dependencies are mocked.
6 | """
7 |
8 | from unittest.mock import AsyncMock, Mock, patch
9 |
10 | import pytest
11 |
12 |
13 | class TestMCPServerImports:
14 | """Test MCP server module imports (SRP: Only tests imports)."""
15 |
16 | def test_mcp_server_module_exists(self):
17 | """Test that mcp_server module can be imported."""
18 | try:
19 | import mcp_server
20 |
21 | assert mcp_server is not None
22 | except ImportError:
23 | pytest.skip("mcp_server module not installed")
24 | except SystemExit:
25 | pytest.skip("MCP dependencies (mcp.server.fastmcp) not available")
26 |
27 |
28 | class TestMCPServerInitialization:
29 | """Test MCP server initialization (SRP: Only tests initialization)."""
30 |
31 | @pytest.mark.asyncio
32 | async def test_mcp_server_can_be_imported(self):
33 | """Basic smoke test: verify MCP server components can be imported."""
34 | try:
35 | from mcp_server import server
36 |
37 | assert server is not None
38 | except ImportError:
39 | pytest.skip("MCP server module not available")
40 | except SystemExit:
41 | pytest.skip("MCP dependencies (mcp.server.fastmcp) not available")
42 | except Exception as e:
43 | # Some initialization errors are acceptable in unit tests
44 | pytest.skip(f"MCP server initialization requires specific setup: {e}")
45 |
```
--------------------------------------------------------------------------------
/docs/content/docs/computer-sdk/computer-server/REST-API.mdx:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | title: REST API Reference
3 | description: Reference for the /cmd REST endpoint of the Computer Server.
4 | ---
5 |
6 | # REST API Reference
7 |
8 | The Computer Server exposes a single REST endpoint for command execution:
9 |
10 | - `http://localhost:8000/cmd`
11 | - `https://your-container.containers.cloud.trycua.com:8443/cmd` (cloud)
12 |
13 | ## POST /cmd
14 |
15 | - Accepts commands as JSON in the request body
16 | - Returns results as a streaming response (text/event-stream)
17 |
18 | ### Request Format
19 |
20 | ```json
21 | {
22 | "command": "<command_name>",
23 | "params": { ... }
24 | }
25 | ```
26 |
27 | ### Required Headers (for cloud containers)
28 |
29 | - `X-Container-Name`: Name of the container (cloud only)
30 | - `X-API-Key`: API key for authentication (cloud only)
31 |
32 | ### Example Request (Python)
33 |
34 | ```python
35 | import requests
36 |
37 | url = "http://localhost:8000/cmd"
38 | body = {"command": "screenshot", "params": {}}
39 | resp = requests.post(url, json=body)
40 | print(resp.text)
41 | ```
42 |
43 | ### Example Request (Cloud)
44 |
45 | ```python
46 | import requests
47 |
48 | url = "https://your-container.containers.cloud.trycua.com:8443/cmd"
49 | headers = {
50 | "X-Container-Name": "your-container",
51 | "X-API-Key": "your-api-key"
52 | }
53 | body = {"command": "screenshot", "params": {}}
54 | resp = requests.post(url, json=body, headers=headers)
55 | print(resp.text)
56 | ```
57 |
58 | ### Response Format
59 |
60 | Streaming text/event-stream with JSON objects, e.g.:
61 |
62 | ```
63 | data: {"success": true, "content": "..."}
64 |
65 | data: {"success": false, "error": "..."}
66 | ```
67 |
68 | ### Supported Commands
69 |
70 | See [Commands Reference](./Commands) for the full list of commands and parameters.
71 |
```
--------------------------------------------------------------------------------
/libs/kasm/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM kasmweb/core-ubuntu-jammy:1.17.0
2 | USER root
3 |
4 | ENV HOME=/home/kasm-default-profile
5 | ENV STARTUPDIR=/dockerstartup
6 | ENV INST_SCRIPTS=$STARTUPDIR/install
7 | WORKDIR $HOME
8 |
9 | ######### Customize Container Here ###########
10 |
11 | # Installing python, pip, and libraries
12 | RUN apt-get update
13 | RUN apt install -y wget build-essential libncursesw5-dev libssl-dev \
14 | libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev libffi-dev zlib1g-dev
15 | RUN add-apt-repository ppa:deadsnakes/ppa
16 | RUN apt install -y python3.11 python3-pip python3-tk python3-dev \
17 | gnome-screenshot wmctrl ffmpeg socat xclip
18 |
19 | RUN pip install cua-computer-server
20 |
21 | # Install Firefox
22 | ENV DEBIAN_FRONTEND=noninteractive \
23 | INST_DIR=$STARTUPDIR/install
24 | COPY ./src/ $INST_DIR
25 | RUN bash ${INST_DIR}/ubuntu/install/firefox/install_firefox.sh
26 |
27 | # Disable SSL requirement
28 | RUN sed -i 's/require_ssl: true/require_ssl: false/g' /usr/share/kasmvnc/kasmvnc_defaults.yaml
29 | RUN sed -i 's/-sslOnly//g' /dockerstartup/vnc_startup.sh
30 |
31 | # Running the python script on startup
32 | RUN echo "/usr/bin/python3 -m computer_server" > $STARTUPDIR/custom_startup.sh \
33 | && chmod +x $STARTUPDIR/custom_startup.sh
34 |
35 | # Enable sudo support for kasm-user
36 | RUN echo "kasm-user:password" | chpasswd
37 | RUN usermod -aG sudo kasm-user
38 | RUN echo "kasm-user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
39 |
40 | ######### End Customizations ###########
41 |
42 | RUN chown 1000:0 $HOME
43 | RUN $STARTUPDIR/set_user_permission.sh $HOME
44 | ENV HOME=/home/kasm-user
45 | WORKDIR $HOME
46 | RUN mkdir -p $HOME && chown -R 1000:0 $HOME
47 |
48 | USER 1000
```
--------------------------------------------------------------------------------
/examples/winsandbox_example.py:
--------------------------------------------------------------------------------
```python
1 | """Example of using the Windows Sandbox computer provider.
2 |
3 | Learn more at: https://learn.microsoft.com/en-us/windows/security/application-security/application-isolation/windows-sandbox/
4 | """
5 |
6 | import asyncio
7 |
8 | from computer import Computer
9 |
10 |
11 | async def main():
12 | """Test the Windows Sandbox provider."""
13 |
14 | # Create a computer instance using Windows Sandbox
15 | computer = Computer(
16 | provider_type="winsandbox",
17 | os_type="windows",
18 | memory="4GB",
19 | # ephemeral=True, # Always true for Windows Sandbox
20 | )
21 |
22 | try:
23 | print("Starting Windows Sandbox...")
24 | await computer.run()
25 |
26 | print("Windows Sandbox is ready!")
27 | print(f"IP Address: {await computer.get_ip()}")
28 |
29 | # Test basic functionality
30 | print("Testing basic functionality...")
31 | screenshot = await computer.interface.screenshot()
32 | print(f"Screenshot taken: {len(screenshot)} bytes")
33 |
34 | # Test running a command
35 | print("Testing command execution...")
36 | result = await computer.interface.run_command("echo Hello from Windows Sandbox!")
37 | print(f"Command output: {result.stdout}")
38 |
39 | print("Press any key to continue...")
40 | input()
41 |
42 | except Exception as e:
43 | print(f"Error: {e}")
44 | import traceback
45 |
46 | traceback.print_exc()
47 |
48 | finally:
49 | print("Stopping Windows Sandbox...")
50 | await computer.stop()
51 | print("Windows Sandbox stopped.")
52 |
53 |
54 | if __name__ == "__main__":
55 | asyncio.run(main())
56 |
```
--------------------------------------------------------------------------------
/libs/python/agent/agent/callbacks/prompt_instructions.py:
--------------------------------------------------------------------------------
```python
1 | """
2 | Prompt instructions callback.
3 |
4 | This callback allows simple prompt engineering by pre-pending a user
5 | instructions message to the start of the conversation before each LLM call.
6 |
7 | Usage:
8 |
9 | from agent.callbacks import PromptInstructionsCallback
10 | agent = ComputerAgent(
11 | model="openai/computer-use-preview",
12 | callbacks=[PromptInstructionsCallback("Follow these rules...")]
13 | )
14 |
15 | """
16 |
17 | from typing import Any, Dict, List, Optional
18 |
19 | from .base import AsyncCallbackHandler
20 |
21 |
22 | class PromptInstructionsCallback(AsyncCallbackHandler):
23 | """
24 | Prepend a user instructions message to the message list.
25 |
26 | This is a minimal, non-invasive way to guide the agent's behavior without
27 | modifying agent loops or tools. It works with any provider/loop since it
28 | only alters the messages array before sending to the model.
29 | """
30 |
31 | def __init__(self, instructions: Optional[str]) -> None:
32 | self.instructions = instructions
33 |
34 | async def on_llm_start(self, messages: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
35 | # Pre-pend instructions message
36 | if not self.instructions:
37 | return messages
38 |
39 | # Ensure we don't duplicate if already present at the front
40 | if messages and isinstance(messages[0], dict):
41 | first = messages[0]
42 | if first.get("role") == "user" and first.get("content") == self.instructions:
43 | return messages
44 |
45 | return [
46 | {"role": "user", "content": self.instructions},
47 | ] + messages
48 |
```
--------------------------------------------------------------------------------
/libs/python/mcp-server/desktop-extension/setup.py:
--------------------------------------------------------------------------------
```python
1 | #!/usr/bin/env python3
2 | """
3 | Setup script for CUA Desktop Extension
4 | Installs required dependencies if not available
5 | """
6 |
7 | import subprocess
8 | import sys
9 |
10 |
11 | def check_and_install_package(package_name, import_name=None):
12 | """Check if a package is available, install if not."""
13 | if import_name is None:
14 | import_name = package_name
15 |
16 | try:
17 | __import__(import_name)
18 | print(f"✓ {package_name} is available")
19 | return True
20 | except ImportError:
21 | print(f"⚠ {package_name} not found, installing...")
22 | try:
23 | subprocess.check_call([sys.executable, "-m", "pip", "install", package_name])
24 | print(f"✓ {package_name} installed successfully")
25 | return True
26 | except subprocess.CalledProcessError as e:
27 | print(f"✗ Failed to install {package_name}: {e}")
28 | return False
29 |
30 |
31 | def main():
32 | """Install required packages."""
33 | print("Setting up CUA Desktop Extension dependencies...")
34 |
35 | # Required packages
36 | packages = [
37 | ("mcp", "mcp"),
38 | ("anyio", "anyio"),
39 | ("cua-agent[all]", "agent"),
40 | ("cua-computer", "computer"),
41 | ]
42 |
43 | all_installed = True
44 | for package, import_name in packages:
45 | if not check_and_install_package(package, import_name):
46 | all_installed = False
47 |
48 | if all_installed:
49 | print("✓ All dependencies are ready!")
50 | return 0
51 | else:
52 | print("✗ Some dependencies failed to install")
53 | return 1
54 |
55 |
56 | if __name__ == "__main__":
57 | sys.exit(main())
58 |
```
--------------------------------------------------------------------------------
/libs/python/mcp-server/scripts/start_mcp_server.sh:
--------------------------------------------------------------------------------
```bash
1 | #!/usr/bin/env bash
2 | set -Eeuo pipefail
3 |
4 | # --- Resolve repo root from this script's location ---
5 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
6 | CUA_REPO_DIR="$( cd "$SCRIPT_DIR/../../../.." &> /dev/null && pwd )"
7 |
8 | # --- Choose a Python interpreter (prefer repo-root venv) ---
9 | CANDIDATES=(
10 | "$CUA_REPO_DIR/.venv/bin/python"
11 | "$CUA_REPO_DIR/libs/.venv/bin/python"
12 | "$(command -v python3 || true)"
13 | "$(command -v python || true)"
14 | )
15 |
16 | PYTHON_PATH=""
17 | for p in "${CANDIDATES[@]}"; do
18 | if [[ -n "$p" && -x "$p" ]]; then
19 | PYTHON_PATH="$p"
20 | break
21 | fi
22 | done
23 |
24 | if [[ -z "${PYTHON_PATH}" ]]; then
25 | >&2 echo "[cua-mcp] ERROR: No suitable Python found. Tried:"
26 | for p in "${CANDIDATES[@]}"; do >&2 echo " - $p"; done
27 | >&2 echo "[cua-mcp] Tip: create venv: python3 -m venv $CUA_REPO_DIR/.venv && \"$CUA_REPO_DIR/.venv/bin/pip\" install -e \"$CUA_REPO_DIR/libs/python/mcp-server\""
28 | exit 127
29 | fi
30 |
31 | # --- Export PYTHONPATH so module imports work during dev ---
32 | export PYTHONPATH="$CUA_REPO_DIR/libs/python/mcp-server:$CUA_REPO_DIR/libs/python/agent:$CUA_REPO_DIR/libs/python/computer:$CUA_REPO_DIR/libs/python/core:$CUA_REPO_DIR/libs/python/pylume"
33 |
34 | # --- Helpful startup log for Claude's mcp.log ---
35 | >&2 echo "[cua-mcp] using python: $PYTHON_PATH"
36 | >&2 echo "[cua-mcp] repo dir : $CUA_REPO_DIR"
37 | >&2 echo "[cua-mcp] PYTHONPATH : $PYTHONPATH"
38 | if [[ -n "${CUA_MODEL_NAME:-}" ]]; then
39 | >&2 echo "[cua-mcp] CUA_MODEL_NAME=$CUA_MODEL_NAME"
40 | fi
41 |
42 | # --- Run the MCP server module ---
43 | exec "$PYTHON_PATH" -m mcp_server.server
44 |
```
--------------------------------------------------------------------------------
/libs/qemu-docker/linux/src/entry.sh:
--------------------------------------------------------------------------------
```bash
1 | #!/bin/bash
2 |
3 | cleanup() {
4 | echo "Received signal, shutting down gracefully..."
5 | if [ -n "$VM_PID" ]; then
6 | kill -TERM "$VM_PID" 2>/dev/null
7 | wait "$VM_PID" 2>/dev/null
8 | fi
9 | exit 0
10 | }
11 |
12 | # Install trap for signals
13 | trap cleanup SIGTERM SIGINT SIGHUP SIGQUIT
14 |
15 | # Start the VM in the background
16 | echo "Starting Ubuntu VM..."
17 | /usr/bin/tini -s /run/entry.sh &
18 | VM_PID=$!
19 | echo "Live stream accessible at localhost:8006"
20 |
21 | echo "Waiting for Ubuntu to boot and CUA computer-server to start..."
22 |
23 | VM_IP=""
24 | while true; do
25 | # Wait for VM and get the IP
26 | if [ -z "$VM_IP" ]; then
27 | VM_IP=$(ps aux | grep dnsmasq | grep -oP '(?<=--dhcp-range=)[0-9.]+' | head -1)
28 | if [ -n "$VM_IP" ]; then
29 | echo "Detected VM IP: $VM_IP"
30 | else
31 | echo "Waiting for VM to start..."
32 | sleep 5
33 | continue
34 | fi
35 | fi
36 |
37 | # Check if server is ready
38 | response=$(curl --write-out '%{http_code}' --silent --output /dev/null $VM_IP:5000/status)
39 |
40 | if [ "${response:-0}" -eq 200 ]; then
41 | break
42 | fi
43 |
44 | echo "Waiting for CUA computer-server to be ready. This might take a while..."
45 | sleep 5
46 | done
47 |
48 | echo "VM is up and running, and the CUA Computer Server is ready!"
49 |
50 | echo "Computer server accessible at localhost:5000"
51 |
52 | # Detect initial setup by presence of custom ISO
53 | CUSTOM_ISO=$(find / -maxdepth 1 -type f -iname "*.iso" -print -quit 2>/dev/null || true)
54 | if [ -n "$CUSTOM_ISO" ]; then
55 | echo "Preparation complete. Shutting down gracefully..."
56 | cleanup
57 | fi
58 |
59 | # Keep container alive for golden image boots
60 | echo "Container running. Press Ctrl+C to stop."
61 | tail -f /dev/null
62 |
```
--------------------------------------------------------------------------------
/docs/content/docs/agent-sdk/callbacks/agent-lifecycle.mdx:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | title: Agent Lifecycle
3 | description: Agent callback lifecycle and hooks
4 | ---
5 |
6 | # Callbacks
7 |
8 | Callbacks provide hooks into the agent lifecycle for extensibility. They're called in a specific order during agent execution.
9 |
10 | ## Callback Lifecycle
11 |
12 | ### 1. `on_run_start(kwargs, old_items)`
13 |
14 | Called once when agent run begins. Initialize tracking, logging, or state.
15 |
16 | ### 2. `on_run_continue(kwargs, old_items, new_items)` → bool
17 |
18 | Called before each iteration. Return `False` to stop execution (e.g., budget limits).
19 |
20 | ### 3. `on_llm_start(messages)` → messages
21 |
22 | Preprocess messages before LLM call. Use for PII anonymization, image retention.
23 |
24 | ### 4. `on_api_start(kwargs)`
25 |
26 | Called before each LLM API call.
27 |
28 | ### 5. `on_api_end(kwargs, result)`
29 |
30 | Called after each LLM API call completes.
31 |
32 | ### 6. `on_usage(usage)`
33 |
34 | Called when usage information is received from LLM.
35 |
36 | ### 7. `on_llm_end(messages)` → messages
37 |
38 | Postprocess messages after LLM call. Use for PII deanonymization.
39 |
40 | ### 8. `on_responses(kwargs, responses)`
41 |
42 | Called when responses are received from agent loop.
43 |
44 | ### 9. Response-specific hooks:
45 |
46 | - `on_text(item)` - Text messages
47 | - `on_computer_call_start(item)` - Before computer actions
48 | - `on_computer_call_end(item, result)` - After computer actions
49 | - `on_function_call_start(item)` - Before function calls
50 | - `on_function_call_end(item, result)` - After function calls
51 | - `on_screenshot(screenshot, name)` - When screenshots are taken
52 |
53 | ### 10. `on_run_end(kwargs, old_items, new_items)`
54 |
55 | Called when agent run completes. Finalize tracking, save trajectories.
56 |
```
--------------------------------------------------------------------------------
/libs/python/bench-ui/examples/gui/styles.css:
--------------------------------------------------------------------------------
```css
1 | body {
2 | font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
3 | margin: 0;
4 | padding: 20px;
5 | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
6 | min-height: 100vh;
7 | display: flex;
8 | justify-content: center;
9 | align-items: center;
10 | }
11 |
12 | .container {
13 | background: white;
14 | border-radius: 12px;
15 | padding: 40px;
16 | box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
17 | max-width: 600px;
18 | width: 100%;
19 | }
20 |
21 | h1 {
22 | color: #333;
23 | margin-top: 0;
24 | font-size: 2em;
25 | }
26 |
27 | p {
28 | color: #666;
29 | line-height: 1.6;
30 | }
31 |
32 | .image-container {
33 | display: flex;
34 | justify-content: center;
35 | margin: 30px 0;
36 | }
37 |
38 | .logo {
39 | width: 150px;
40 | height: 150px;
41 | }
42 |
43 | .info {
44 | background: #f8f9fa;
45 | border-left: 4px solid #667eea;
46 | padding: 20px;
47 | margin: 20px 0;
48 | border-radius: 4px;
49 | }
50 |
51 | .info ul {
52 | margin: 10px 0;
53 | padding-left: 20px;
54 | }
55 |
56 | .info li {
57 | color: #555;
58 | margin: 8px 0;
59 | }
60 |
61 | .btn {
62 | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
63 | color: white;
64 | border: none;
65 | padding: 12px 30px;
66 | font-size: 16px;
67 | border-radius: 6px;
68 | cursor: pointer;
69 | transition: transform 0.2s, box-shadow 0.2s;
70 | font-weight: 600;
71 | }
72 |
73 | .btn:hover:not(:disabled) {
74 | transform: translateY(-2px);
75 | box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
76 | }
77 |
78 | .btn:active:not(:disabled) {
79 | transform: translateY(0);
80 | }
81 |
82 | .btn:disabled {
83 | opacity: 0.6;
84 | cursor: not-allowed;
85 | }
86 |
87 | #status {
88 | margin-top: 15px;
89 | font-weight: 600;
90 | color: #28a745;
91 | font-size: 18px;
92 | }
```
--------------------------------------------------------------------------------
/libs/python/computer-server/computer_server/diorama/macos.py:
--------------------------------------------------------------------------------
```python
1 | import inspect
2 | import platform
3 | import sys
4 | from typing import Optional
5 |
6 | from computer_server.diorama.base import BaseDioramaHandler
7 | from computer_server.diorama.diorama import Diorama
8 |
9 |
10 | class MacOSDioramaHandler(BaseDioramaHandler):
11 | """Handler for Diorama commands on macOS, using local diorama module."""
12 |
13 | async def diorama_cmd(self, action: str, arguments: Optional[dict] = None) -> dict:
14 | if platform.system().lower() != "darwin":
15 | return {"success": False, "error": "Diorama is only supported on macOS."}
16 | try:
17 | app_list = arguments.get("app_list") if arguments else None
18 | if not app_list:
19 | return {"success": False, "error": "Missing 'app_list' in arguments"}
20 | diorama = Diorama(app_list)
21 | interface = diorama.interface
22 | if not hasattr(interface, action):
23 | return {"success": False, "error": f"Unknown diorama action: {action}"}
24 | method = getattr(interface, action)
25 | # Remove app_list from arguments before calling the method
26 | filtered_arguments = dict(arguments)
27 | filtered_arguments.pop("app_list", None)
28 | if inspect.iscoroutinefunction(method):
29 | result = await method(**(filtered_arguments or {}))
30 | else:
31 | result = method(**(filtered_arguments or {}))
32 | return {"success": True, "result": result}
33 | except Exception as e:
34 | import traceback
35 |
36 | return {"success": False, "error": str(e), "trace": traceback.format_exc()}
37 |
```
--------------------------------------------------------------------------------
/docs/src/lib/source.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { docs } from '@/.source';
2 | import { loader } from 'fumadocs-core/source';
3 | import { icons } from 'lucide-react';
4 | import { createElement } from 'react';
5 |
6 | import fs from 'node:fs/promises';
7 | import path from 'node:path';
8 |
9 | /**
10 | * Returns available API doc versions for a given section (e.g., 'agent').
11 | * Each version is an object: { label, slug }
12 | * - 'Current' (index.mdx) → slug: []
13 | * - '[version].mdx' → slug: [version]
14 | */
15 | export async function getApiVersions(
16 | section: string
17 | ): Promise<{ label: string; slug: string[] }[]> {
18 | const dir = path.join(process.cwd(), 'content/docs/api', section);
19 | let files: string[] = [];
20 | try {
21 | files = (await fs.readdir(dir)).filter((f) => f.endsWith('.mdx'));
22 | } catch (_e) {
23 | return [];
24 | }
25 | const versions = files.map((file) => {
26 | if (file === 'index.mdx') {
27 | return { label: 'Current', slug: [] };
28 | }
29 | const version = file.replace(/\.mdx$/, '');
30 | return { label: version, slug: [version] };
31 | });
32 | // Always put 'Current' first, then others sorted descending (semver-ish)
33 | return [
34 | ...versions.filter((v) => v.label === 'Current'),
35 | ...versions
36 | .filter((v) => v.label !== 'Current')
37 | .sort((a, b) => b.label.localeCompare(a.label, undefined, { numeric: true })),
38 | ];
39 | }
40 |
41 | // See https://fumadocs.vercel.app/docs/headless/source-api for more info
42 | export const source = loader({
43 | // it assigns a URL to your pages
44 | baseUrl: '/',
45 | source: docs.toFumadocsSource(),
46 | icon(icon) {
47 | if (!icon) return;
48 | if (icon in icons) return createElement(icons[icon as keyof typeof icons]);
49 | },
50 | });
51 |
```
--------------------------------------------------------------------------------
/libs/python/som/pyproject.toml:
--------------------------------------------------------------------------------
```toml
1 | [build-system]
2 | requires = ["pdm-backend"]
3 | build-backend = "pdm.backend"
4 |
5 | [project]
6 | name = "cua-som"
7 | version = "0.1.3"
8 | description = "Computer Vision and OCR library for detecting and analyzing UI elements"
9 | authors = [
10 | { name = "TryCua", email = "[email protected]" }
11 | ]
12 | dependencies = [
13 | "torch>=2.2.1",
14 | "torchvision>=0.17.1",
15 | "ultralytics>=8.1.28",
16 | "easyocr>=1.7.1",
17 | "numpy>=1.26.4",
18 | "pillow>=10.2.0",
19 | "setuptools>=75.8.1",
20 | "opencv-python-headless>=4.11.0.86",
21 | "matplotlib>=3.8.3",
22 | "huggingface-hub>=0.21.4",
23 | "supervision>=0.25.1",
24 | "typing-extensions>=4.9.0",
25 | "pydantic>=2.6.3"
26 | ]
27 | requires-python = ">=3.12,<3.14"
28 | readme = "README.md"
29 | license = {text = "AGPL-3.0-or-later"}
30 | keywords = ["computer-vision", "ocr", "ui-analysis", "icon-detection"]
31 | classifiers = [
32 | "Development Status :: 4 - Beta",
33 | "License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)",
34 | "Intended Audience :: Developers",
35 | "Programming Language :: Python :: 3",
36 | "Programming Language :: Python :: 3.11",
37 | "Topic :: Scientific/Engineering :: Artificial Intelligence",
38 | "Topic :: Scientific/Engineering :: Image Recognition"
39 | ]
40 |
41 | [project.urls]
42 | Homepage = "https://github.com/trycua/cua"
43 | Repository = "https://github.com/trycua/cua"
44 | Documentation = "https://github.com/trycua/cua/tree/main/docs"
45 |
46 | [tool.pdm]
47 | distribution = true
48 | package-type = "library"
49 | src-layout = false
50 |
51 | [tool.pdm.build]
52 | includes = ["som/"]
53 | source-includes = ["tests/", "README.md", "LICENSE"]
54 |
55 | [tool.pytest.ini_options]
56 | asyncio_mode = "auto"
57 | testpaths = ["tests"]
58 | python_files = "test_*.py"
```
--------------------------------------------------------------------------------
/libs/python/computer-server/computer_server/diorama/diorama_computer.py:
--------------------------------------------------------------------------------
```python
1 | import asyncio
2 |
3 |
4 | class DioramaComputer:
5 | """
6 | A minimal Computer-like interface for Diorama, compatible with ComputerAgent.
7 | Implements _initialized, run(), and __aenter__ for agent compatibility.
8 | """
9 |
10 | def __init__(self, diorama):
11 | """
12 | Initialize the DioramaComputer with a diorama instance.
13 |
14 | Args:
15 | diorama: The diorama instance to wrap with a computer-like interface.
16 | """
17 | self.diorama = diorama
18 | self.interface = self.diorama.interface
19 | self._initialized = False
20 |
21 | async def __aenter__(self):
22 | """
23 | Async context manager entry method for compatibility with ComputerAgent.
24 |
25 | Ensures an event loop is running and marks the instance as initialized.
26 | Creates a new event loop if none is currently running.
27 |
28 | Returns:
29 | DioramaComputer: The initialized instance.
30 | """
31 | # Ensure the event loop is running (for compatibility)
32 | try:
33 | asyncio.get_running_loop()
34 | except RuntimeError:
35 | asyncio.set_event_loop(asyncio.new_event_loop())
36 | self._initialized = True
37 | return self
38 |
39 | async def run(self):
40 | """
41 | Run method stub for compatibility with ComputerAgent interface.
42 |
43 | Ensures the instance is initialized before returning. If not already
44 | initialized, calls __aenter__ to perform initialization.
45 |
46 | Returns:
47 | DioramaComputer: The initialized instance.
48 | """
49 | # This is a stub for compatibility
50 | if not self._initialized:
51 | await self.__aenter__()
52 | return self
53 |
```
--------------------------------------------------------------------------------
/libs/lume/src/VM/VMDetails.swift:
--------------------------------------------------------------------------------
```swift
1 | import Foundation
2 | import Network
3 |
4 | struct DiskSize: Codable {
5 | let allocated: UInt64
6 | let total: UInt64
7 | }
8 |
9 | extension DiskSize {
10 | var formattedAllocated: String {
11 | formatBytes(allocated)
12 | }
13 |
14 | var formattedTotal: String {
15 | formatBytes(total)
16 | }
17 |
18 | private func formatBytes(_ bytes: UInt64) -> String {
19 | let units = ["B", "KB", "MB", "GB", "TB"]
20 | var size = Double(bytes)
21 | var unitIndex = 0
22 |
23 | while size >= 1024 && unitIndex < units.count - 1 {
24 | size /= 1024
25 | unitIndex += 1
26 | }
27 |
28 | return String(format: "%.1f%@", size, units[unitIndex])
29 | }
30 | }
31 |
32 | struct VMDetails: Codable {
33 | let name: String
34 | let os: String
35 | let cpuCount: Int
36 | let memorySize: UInt64
37 | let diskSize: DiskSize
38 | let display: String
39 | let status: String
40 | let vncUrl: String?
41 | let ipAddress: String?
42 | let locationName: String
43 | let sharedDirectories: [SharedDirectory]?
44 |
45 | init(
46 | name: String,
47 | os: String,
48 | cpuCount: Int,
49 | memorySize: UInt64,
50 | diskSize: DiskSize,
51 | display: String,
52 | status: String,
53 | vncUrl: String?,
54 | ipAddress: String?,
55 | locationName: String,
56 | sharedDirectories: [SharedDirectory]? = nil
57 | ) {
58 | self.name = name
59 | self.os = os
60 | self.cpuCount = cpuCount
61 | self.memorySize = memorySize
62 | self.diskSize = diskSize
63 | self.display = display
64 | self.status = status
65 | self.vncUrl = vncUrl
66 | self.ipAddress = ipAddress
67 | self.locationName = locationName
68 | self.sharedDirectories = sharedDirectories
69 | }
70 | }
71 |
```
--------------------------------------------------------------------------------
/examples/sandboxed_functions_examples.py:
--------------------------------------------------------------------------------
```python
1 | import os
2 | import sys
3 | from pathlib import Path
4 |
5 | # Load environment variables from .env file
6 | project_root = Path(__file__).parent.parent
7 | env_file = project_root / ".env"
8 | print(f"Loading environment from: {env_file}")
9 | from dotenv import load_dotenv
10 |
11 | load_dotenv(env_file)
12 |
13 | # Add paths to sys.path if needed
14 | pythonpath = os.environ.get("PYTHONPATH", "")
15 | for path in pythonpath.split(":"):
16 | if path and path not in sys.path:
17 | sys.path.insert(0, path) # Insert at beginning to prioritize
18 | print(f"Added to sys.path: {path}")
19 |
20 | import asyncio
21 |
22 | from computer.computer import Computer
23 | from computer.helpers import sandboxed
24 |
25 |
26 | async def main():
27 | # Initialize the computer in a Cua Container
28 | computer = Computer()
29 | await computer.run()
30 |
31 | # Install a package in a virtual environment in the container
32 | await computer.venv_install("demo_venv", ["requests", "macos-pyxa"])
33 |
34 | # Open Safari
35 | await computer.interface.run_command("open -a Safari")
36 | await asyncio.sleep(2)
37 |
38 | # Define a sandboxed function
39 | # This function will run inside the Cua Container
40 | @sandboxed("demo_venv")
41 | def greet_and_print(name):
42 | # get .html of the current Safari tab
43 | import PyXA
44 |
45 | safari = PyXA.Application("Safari")
46 | current_doc = safari.current_document
47 | html = current_doc.source()
48 | print(f"Hello from inside the container, {name}!")
49 | print("Safari HTML length:", len(html))
50 | return {"greeted": name, "safari_html_length": len(html), "safari_html_snippet": html[:200]}
51 |
52 | # Call with args and kwargs
53 | result = await greet_and_print("Cua")
54 | print("Result from sandboxed function:", result)
55 |
56 |
57 | if __name__ == "__main__":
58 | asyncio.run(main())
59 |
```
--------------------------------------------------------------------------------
/libs/lumier/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | # Base image using Debian for arm64 architecture (optimized for Apple Silicon)
2 | FROM debian:bullseye-slim AS lumier-base
3 |
4 | # Set environment variables for Lume API server configuration
5 | ENV LUME_API_HOST="host.docker.internal"
6 |
7 | # Default VM configuration (can be overridden at runtime)
8 | ENV VERSION="ghcr.io/trycua/macos-sequoia-vanilla:latest"
9 | ENV RAM_SIZE="8192"
10 | ENV CPU_CORES="4"
11 | ENV DISK_SIZE="100"
12 | ENV DISPLAY="1024x768"
13 | ENV VM_NAME="lumier"
14 | ENV HOST_SHARED_PATH=""
15 | ENV LUMIER_DEBUG="0"
16 |
17 | # Install necessary tools and noVNC dependencies
18 | RUN apt-get update && \
19 | apt-get install -y \
20 | netcat-traditional \
21 | curl \
22 | sshpass \
23 | wget \
24 | unzip \
25 | git \
26 | python3 \
27 | python3-pip \
28 | python3-numpy \
29 | procps && \
30 | rm -rf /var/lib/apt/lists/*
31 |
32 | # Download and install noVNC without caching
33 | RUN wget https://github.com/trycua/noVNC/archive/refs/heads/master.zip -O master1.zip && \
34 | unzip master1.zip && \
35 | mv noVNC-master /opt/noVNC && \
36 | rm master1.zip
37 |
38 | # Set environment variables for noVNC
39 | ENV NOVNC_PATH="/opt/noVNC"
40 |
41 | # Create necessary directories
42 | RUN mkdir -p /run/bin /run/lib /run/config /run/hooks /run/lifecycle
43 |
44 | # Copy scripts to the container
45 | COPY src/config/constants.sh /run/config/
46 | COPY src/bin/entry.sh /run/bin/entry.sh
47 |
48 | # Copy library files if they exist
49 | COPY src/lib/ /run/lib/
50 | COPY src/hooks/ /run/hooks/
51 |
52 | # Copy on-logon script to lifecycle directory
53 | COPY src/hooks/on-logon.sh /run/lifecycle/
54 |
55 | # Make scripts executable
56 | RUN chmod +x \
57 | /run/bin/* \
58 | /run/hooks/* \
59 | /run/lifecycle/* 2>/dev/null || true
60 |
61 | # Expose ports for noVNC and Lume API
62 | EXPOSE 8006
63 |
64 | # VOLUME setup
65 | VOLUME [ "/storage" ]
66 | VOLUME [ "/data" ]
67 |
68 | # Default entrypoint
69 | ENTRYPOINT ["/run/bin/entry.sh"]
```
--------------------------------------------------------------------------------
/.github/workflows/pypi-publish-core.yml:
--------------------------------------------------------------------------------
```yaml
1 | name: Publish Core Package
2 |
3 | on:
4 | push:
5 | tags:
6 | - "core-v*"
7 | workflow_dispatch:
8 | inputs:
9 | version:
10 | description: "Version to publish (without v prefix)"
11 | required: true
12 | default: "0.1.0"
13 | workflow_call:
14 | inputs:
15 | version:
16 | description: "Version to publish"
17 | required: true
18 | type: string
19 |
20 | # Adding permissions at workflow level
21 | permissions:
22 | contents: write
23 |
24 | jobs:
25 | prepare:
26 | runs-on: macos-latest
27 | outputs:
28 | version: ${{ steps.get-version.outputs.version }}
29 | steps:
30 | - uses: actions/checkout@v4
31 |
32 | - name: Determine version
33 | id: get-version
34 | run: |
35 | if [ "${{ github.event_name }}" == "push" ]; then
36 | # Extract version from tag (for package-specific tags)
37 | if [[ "${{ github.ref }}" =~ ^refs/tags/core-v([0-9]+\.[0-9]+\.[0-9]+) ]]; then
38 | VERSION=${BASH_REMATCH[1]}
39 | else
40 | echo "Invalid tag format for core"
41 | exit 1
42 | fi
43 | elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
44 | # Use version from workflow dispatch
45 | VERSION=${{ github.event.inputs.version }}
46 | else
47 | # Use version from workflow_call
48 | VERSION=${{ inputs.version }}
49 | fi
50 | echo "VERSION=$VERSION"
51 | echo "version=$VERSION" >> $GITHUB_OUTPUT
52 |
53 | publish:
54 | needs: prepare
55 | uses: ./.github/workflows/pypi-reusable-publish.yml
56 | with:
57 | package_name: "core"
58 | package_dir: "libs/python/core"
59 | version: ${{ needs.prepare.outputs.version }}
60 | is_lume_package: false
61 | base_package_name: "cua-core"
62 | secrets:
63 | PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
64 |
```
--------------------------------------------------------------------------------
/libs/lume/src/VM/LinuxVM.swift:
--------------------------------------------------------------------------------
```swift
1 | import Foundation
2 |
3 | /// Linux-specific virtual machine implementation
4 | @MainActor
5 | final class LinuxVM: VM {
6 | override init(
7 | vmDirContext: VMDirContext,
8 | virtualizationServiceFactory: @escaping (VMVirtualizationServiceContext) throws -> VMVirtualizationService = { try LinuxVirtualizationService(configuration: $0) },
9 | vncServiceFactory: @escaping (VMDirectory) -> VNCService = { DefaultVNCService(vmDirectory: $0) }
10 | ) {
11 | super.init(
12 | vmDirContext: vmDirContext,
13 | virtualizationServiceFactory: virtualizationServiceFactory,
14 | vncServiceFactory: vncServiceFactory
15 | )
16 | }
17 |
18 | override func getOSType() -> String {
19 | return "linux"
20 | }
21 |
22 | override func setup(
23 | ipswPath: String,
24 | cpuCount: Int,
25 | memorySize: UInt64,
26 | diskSize: UInt64,
27 | display: String
28 | ) async throws {
29 |
30 | try setDiskSize(diskSize)
31 |
32 | let service = try virtualizationServiceFactory(
33 | try createVMVirtualizationServiceContext(
34 | cpuCount: cpuCount,
35 | memorySize: memorySize,
36 | display: display
37 | )
38 | )
39 | guard let linuxService = service as? LinuxVirtualizationService else {
40 | throw VMError.internalError("Installation requires LinuxVirtualizationService")
41 | }
42 |
43 | try updateVMConfig(vmConfig: try VMConfig(
44 | os: getOSType(),
45 | cpuCount: cpuCount,
46 | memorySize: memorySize,
47 | diskSize: diskSize,
48 | macAddress: linuxService.generateMacAddress(),
49 | display: display
50 | ))
51 |
52 | // Create NVRAM store for EFI
53 | try linuxService.createNVRAM(at: vmDirContext.nvramPath)
54 | }
55 | }
```
--------------------------------------------------------------------------------
/libs/lume/tests/Mocks/MockVMVirtualizationService.swift:
--------------------------------------------------------------------------------
```swift
1 | import Foundation
2 | import Virtualization
3 | @testable import lume
4 |
5 | @MainActor
6 | final class MockVMVirtualizationService: VMVirtualizationService {
7 | private(set) var currentState: VZVirtualMachine.State = .stopped
8 | private(set) var startCallCount = 0
9 | private(set) var stopCallCount = 0
10 | private(set) var pauseCallCount = 0
11 | private(set) var resumeCallCount = 0
12 |
13 | var state: VZVirtualMachine.State {
14 | currentState
15 | }
16 |
17 | private var _shouldFailNextOperation = false
18 | private var _operationError: Error = VMError.internalError("Mock operation failed")
19 |
20 | nonisolated func configure(shouldFail: Bool, error: Error = VMError.internalError("Mock operation failed")) async {
21 | await setConfiguration(shouldFail: shouldFail, error: error)
22 | }
23 |
24 | @MainActor
25 | private func setConfiguration(shouldFail: Bool, error: Error) {
26 | _shouldFailNextOperation = shouldFail
27 | _operationError = error
28 | }
29 |
30 | func start() async throws {
31 | startCallCount += 1
32 | if _shouldFailNextOperation {
33 | throw _operationError
34 | }
35 | currentState = .running
36 | }
37 |
38 | func stop() async throws {
39 | stopCallCount += 1
40 | if _shouldFailNextOperation {
41 | throw _operationError
42 | }
43 | currentState = .stopped
44 | }
45 |
46 | func pause() async throws {
47 | pauseCallCount += 1
48 | if _shouldFailNextOperation {
49 | throw _operationError
50 | }
51 | currentState = .paused
52 | }
53 |
54 | func resume() async throws {
55 | resumeCallCount += 1
56 | if _shouldFailNextOperation {
57 | throw _operationError
58 | }
59 | currentState = .running
60 | }
61 |
62 | func getVirtualMachine() -> Any {
63 | return "mock_vm"
64 | }
65 | }
```
--------------------------------------------------------------------------------
/docs/content/docs/macos-vm-cli-playbook/lume/installation.mdx:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | title: Installation
3 | description: Installation instructions for the current version of the Lume CLI.
4 | ---
5 |
6 | ## Quickstart
7 |
8 | Install and run a prebuilt macOS sandbox in two commands:
9 |
10 | ```bash
11 | # Install Lume
12 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/trycua/cua/main/libs/lume/scripts/install.sh)"
13 | # Pull & start a macOS image
14 | lume run macos-sequoia-vanilla:latest
15 | ```
16 |
17 | <Callout title="Security Note">
18 | All prebuilt images use the default password `lume`. Change this immediately after your first
19 | login using the `passwd` command.
20 | </Callout>
21 |
22 | **System Requirements**:
23 |
24 | - Apple Silicon Mac (M1, M2, M3, etc.)
25 | - macOS 13.0 or later
26 | - At least 8GB of RAM (16GB recommended)
27 | - At least 50GB of free disk space
28 |
29 | ## Install with Script
30 |
31 | Install with a single command:
32 |
33 | ```bash
34 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/trycua/cua/main/libs/lume/scripts/install.sh)"
35 | ```
36 |
37 | ### Manual Start (No Background Service)
38 |
39 | By default, Lume is installed as a background service that starts automatically on login. If you prefer to start the Lume API service manually when needed, you can use the `--no-background-service` option:
40 |
41 | ```bash
42 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/trycua/cua/main/libs/lume/scripts/install.sh) --no-background-service"
43 | ```
44 |
45 | <Callout title="Note">
46 | With this option, you'll need to manually start the Lume API service by running `lume serve` in
47 | your terminal whenever you need to use tools or libraries that rely on the Lume API (such as the
48 | Computer-Use Agent).
49 | </Callout>
50 |
51 | ## Manual Download and Installation
52 |
53 | You can also download the `lume.pkg.tar.gz` archive from the [latest release](https://github.com/trycua/cua/releases?q=lume&expanded=true), extract it, and install the package manually.
54 |
```
--------------------------------------------------------------------------------
/libs/qemu-docker/windows/src/entry.sh:
--------------------------------------------------------------------------------
```bash
1 | #!/bin/bash
2 |
3 | cleanup() {
4 | echo "Received signal, shutting down gracefully..."
5 | if [ -n "$VM_PID" ]; then
6 | kill -TERM "$VM_PID" 2>/dev/null
7 | wait "$VM_PID" 2>/dev/null
8 | fi
9 | exit 0
10 | }
11 |
12 | # Install trap for signals
13 | trap cleanup SIGTERM SIGINT SIGHUP SIGQUIT
14 |
15 | # Create windows.boot file if it doesn't exist (required for proper boot)
16 | if [ -d "/storage" -a ! -f "/storage/windows.boot" ]; then
17 | echo "Creating windows.boot file in /storage..."
18 | touch /storage/windows.boot
19 | fi
20 |
21 | # Start the VM in the background
22 | echo "Starting Windows VM..."
23 | /usr/bin/tini -s /run/entry.sh &
24 | VM_PID=$!
25 | echo "Live stream accessible at localhost:8006"
26 |
27 | echo "Waiting for Windows to boot and CUA computer-server to start..."
28 |
29 | VM_IP=""
30 | while true; do
31 | # Wait from VM and get the IP
32 | if [ -z "$VM_IP" ]; then
33 | VM_IP=$(ps aux | grep dnsmasq | grep -oP '(?<=--dhcp-range=)[0-9.]+' | head -1)
34 | if [ -n "$VM_IP" ]; then
35 | echo "Detected VM IP: $VM_IP"
36 | else
37 | echo "Waiting for VM to start..."
38 | sleep 5
39 | continue
40 | fi
41 | fi
42 |
43 | # Check if server is ready
44 | response=$(curl --write-out '%{http_code}' --silent --output /dev/null $VM_IP:5000/status)
45 |
46 | if [ "${response:-0}" -eq 200 ]; then
47 | break
48 | fi
49 |
50 | echo "Waiting for CUA computer-server to be ready. This might take a while..."
51 | sleep 5
52 | done
53 |
54 | echo "VM is up and running, and the CUA Computer Server is ready!"
55 |
56 | echo "Computer server accessible at localhost:5000"
57 |
58 | # Detect initial setup by presence of custom ISO
59 | CUSTOM_ISO=$(find / -maxdepth 1 -type f -iname "*.iso" -print -quit 2>/dev/null || true)
60 | if [ -n "$CUSTOM_ISO" ]; then
61 | echo "Preparation complete. Shutting down gracefully..."
62 | cleanup
63 | fi
64 |
65 | # Keep container alive for golden image boots
66 | echo "Container running. Press Ctrl+C to stop."
67 | tail -f /dev/null
```
--------------------------------------------------------------------------------
/docs/content/docs/agent-sdk/usage-tracking.mdx:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | title: Usage Tracking
3 | sidebar_position: 9
4 | description: How to track token usage and cost in ComputerAgent and agent loops.
5 | ---
6 |
7 | Tracking usage is important for monitoring costs and optimizing your agent workflows. The ComputerAgent API provides easy access to token and cost usage for every run.
8 |
9 | ## Accessing Usage Data
10 |
11 | Whenever you run an agent loop, each result contains a `usage` dictionary with token and cost information:
12 |
13 | ```python
14 | async for result in agent.run(...):
15 | print(result["usage"])
16 | # Example output:
17 | # {
18 | # "prompt_tokens": 150,
19 | # "completion_tokens": 75,
20 | # "total_tokens": 225,
21 | # "response_cost": 0.01,
22 | # }
23 | ```
24 |
25 | - `prompt_tokens`: Number of tokens in the prompt
26 | - `completion_tokens`: Number of tokens in the agent's response
27 | - `total_tokens`: Total tokens used
28 | - `response_cost`: Estimated cost (USD) for this turn
29 |
30 | ## Tracking Total Usage
31 |
32 | You can accumulate usage across multiple turns:
33 |
34 | ```python
35 | total_usage = {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0, "response_cost": 0.0}
36 | async for result in agent.run(...):
37 | for k in total_usage:
38 | total_usage[k] += result["usage"].get(k, 0)
39 | print("Total usage:", total_usage)
40 | ```
41 |
42 | ## Using Callbacks for Usage Tracking
43 |
44 | You can also use a callback to automatically track usage. Implement the `on_usage` method in your callback class:
45 |
46 | ```python
47 | from agent.callbacks import AsyncCallbackHandler
48 |
49 | class UsageTrackerCallback(AsyncCallbackHandler):
50 | async def on_usage(self, usage):
51 | print("Usage update:", usage)
52 |
53 | agent = ComputerAgent(
54 | ...,
55 | callbacks=[UsageTrackerCallback()]
56 | )
57 | ```
58 |
59 | See also: [Budget Manager Callbacks](./callbacks/cost-saving)
60 |
61 | ## See Also
62 |
63 | - [Prompt Caching](./prompt-caching)
64 | - [Callbacks](./callbacks)
65 |
```
--------------------------------------------------------------------------------
/libs/qemu-docker/windows/src/vm/setup/setup.ps1:
--------------------------------------------------------------------------------
```
1 | $ErrorActionPreference = "Continue"
2 |
3 | $scriptFolder = "C:\OEM"
4 |
5 | Import-Module (Join-Path $scriptFolder -ChildPath "setup-utils.psm1")
6 |
7 | Set-StrictMode -Version Latest
8 |
9 | # Set TLS version to 1.2 or higher
10 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls13
11 |
12 | # Install Git via Chocolatey
13 | $ChocoExe = Resolve-ChocoPath
14 | if ($ChocoExe) {
15 | Write-Host "Installing Git via Chocolatey..."
16 | try {
17 | & $ChocoExe install -y git | Out-Null
18 | Add-ToEnvPath -NewPath "C:\Program Files\Git\bin"
19 | Write-Host "Git installed successfully."
20 | } catch {
21 | Write-Host "Git install warning: $($_.Exception.Message)"
22 | }
23 | } else {
24 | Write-Host "Chocolatey not available; skipping Git install"
25 | }
26 |
27 | # CUA Computer Server Setup
28 | Write-Host "Setting up CUA Computer Server..."
29 | $cuaServerSetupScript = Join-Path $scriptFolder -ChildPath "setup-cua-server.ps1"
30 | if (Test-Path $cuaServerSetupScript) {
31 | & $cuaServerSetupScript
32 | Write-Host "CUA Computer Server setup completed."
33 | } else {
34 | Write-Host "ERROR: setup-cua-server.ps1 not found at $cuaServerSetupScript"
35 | }
36 |
37 | # Register on-logon task
38 | $onLogonTaskName = "WindowsArena_OnLogon"
39 | $onLogonScriptPath = "$scriptFolder\on-logon.ps1"
40 | if (Get-ScheduledTask -TaskName $onLogonTaskName -ErrorAction SilentlyContinue) {
41 | Write-Host "Scheduled task $onLogonTaskName already exists."
42 | } else {
43 | Write-Host "Registering new task $onLogonTaskName..."
44 | Register-LogonTask -TaskName $onLogonTaskName -ScriptPath $onLogonScriptPath -LocalUser "Docker"
45 | }
46 |
47 | Start-Sleep -Seconds 10
48 | Write-Host "Starting $onLogonTaskName task in background..."
49 | Start-Process -WindowStyle Hidden -FilePath "powershell.exe" -ArgumentList "-Command", "Start-ScheduledTask -TaskName '$onLogonTaskName'"
```
--------------------------------------------------------------------------------
/docs/content/docs/agent-sdk/supported-model-providers/index.mdx:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | title: Supported Model Providers
3 | ---
4 |
5 | ## Supported Models
6 |
7 | ### CUA VLM Router (Recommended)
8 |
9 | Use CUA's cloud inference API for intelligent routing and cost optimization with a single API key. CUA manages all provider infrastructure and credentials for you.
10 |
11 | ```python
12 | model="cua/anthropic/claude-sonnet-4.5" # Claude Sonnet 4.5 (recommended)
13 | model="cua/anthropic/claude-haiku-4.5" # Claude Haiku 4.5 (faster)
14 | ```
15 |
16 | **Benefits:**
17 |
18 | - Single API key for multiple providers
19 | - Cost tracking and optimization
20 | - Fully managed infrastructure (no provider keys to manage)
21 |
22 | [Learn more about CUA VLM Router →](/agent-sdk/supported-model-providers/cua-vlm-router)
23 |
24 | ---
25 |
26 | ### Anthropic Claude (Computer Use API - BYOK)
27 |
28 | Direct access to Anthropic's Claude models using your own Anthropic API key (BYOK - Bring Your Own Key).
29 |
30 | ```python
31 | model="anthropic/claude-3-7-sonnet-20250219"
32 | model="anthropic/claude-opus-4-20250514"
33 | model="anthropic/claude-sonnet-4-20250514"
34 | ```
35 |
36 | **Setup:** Set `ANTHROPIC_API_KEY` environment variable with your Anthropic API key.
37 |
38 | ### OpenAI Computer Use Preview (BYOK)
39 |
40 | Direct access to OpenAI's computer use models using your own OpenAI API key (BYOK).
41 |
42 | ```python
43 | model="openai/computer-use-preview"
44 | ```
45 |
46 | **Setup:** Set `OPENAI_API_KEY` environment variable with your OpenAI API key.
47 |
48 | ### UI-TARS (Local or Huggingface Inference)
49 |
50 | Run UI-TARS models locally for privacy and offline use.
51 |
52 | ```python
53 | model="huggingface-local/ByteDance-Seed/UI-TARS-1.5-7B"
54 | model="ollama_chat/0000/ui-tars-1.5-7b"
55 | ```
56 |
57 | ### Omniparser + Any LLM
58 |
59 | Combine Omniparser for UI understanding with any LLM provider.
60 |
61 | ```python
62 | model="omniparser+ollama_chat/mistral-small3.2"
63 | model="omniparser+vertex_ai/gemini-pro"
64 | model="omniparser+anthropic/claude-sonnet-4-5-20250929"
65 | model="omniparser+openai/gpt-4o"
66 | ```
67 |
```
--------------------------------------------------------------------------------
/docs/src/app/layout.config.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
2 |
3 | import Image from 'next/image';
4 | import LogoBlack from '@/assets/logo-black.svg';
5 | import LogoWhite from '@/assets/logo-white.svg';
6 | import DiscordWhite from '@/assets/discord-white.svg';
7 | import DiscordBlack from '@/assets/discord-black.svg';
8 | import { HomeIcon } from 'lucide-react';
9 |
10 | /**
11 | * Shared layout configurations
12 | *
13 | * you can customise layouts individually from:
14 | * Home Layout: app/(home)/layout.tsx
15 | * Docs Layout: app/docs/layout.tsx
16 | */
17 | export const baseOptions: BaseLayoutProps = {
18 | nav: {
19 | title: (
20 | <>
21 | <Image
22 | width={30}
23 | height={30}
24 | src={LogoBlack}
25 | aria-label="Logo"
26 | className="block dark:hidden"
27 | alt="Logo"
28 | />
29 | <Image
30 | width={30}
31 | height={30}
32 | src={LogoWhite}
33 | aria-label="Logo"
34 | className="hidden dark:block"
35 | alt="Logo"
36 | />
37 | Cua
38 | </>
39 | ),
40 | url: 'https://cua.ai',
41 | },
42 | githubUrl: 'https://github.com/trycua/cua',
43 | links: [
44 | {
45 | url: 'https://cua.ai',
46 | text: 'Cua Home',
47 | type: 'icon',
48 | icon: <HomeIcon />,
49 | external: true,
50 | },
51 | {
52 | url: 'https://discord.com/invite/mVnXXpdE85',
53 | text: 'Discord',
54 | type: 'icon',
55 | icon: (
56 | <>
57 | <Image
58 | width={20}
59 | height={20}
60 | alt="Discord"
61 | className="hidden dark:block opacity-70 hover:opacity-100"
62 | src={DiscordWhite}
63 | />
64 | <Image
65 | width={20}
66 | height={20}
67 | alt="Discord"
68 | className="dark:hidden block opacity-55 hover:opacity-100"
69 | src={DiscordBlack}
70 | />
71 | </>
72 | ),
73 | external: true,
74 | },
75 | ],
76 | };
77 |
```
--------------------------------------------------------------------------------
/docs/src/components/analytics-tracker.tsx:
--------------------------------------------------------------------------------
```typescript
1 | 'use client';
2 |
3 | import { useEffect } from 'react';
4 | import posthog from 'posthog-js';
5 |
6 | export function AnalyticsTracker() {
7 | useEffect(() => {
8 | const handleClick = (e: MouseEvent) => {
9 | const target = e.target as HTMLElement;
10 | const link = target.closest('a');
11 |
12 | if (!link) return;
13 |
14 | const href = link.href;
15 | const text = link.textContent || link.getAttribute('aria-label') || '';
16 |
17 | if (href.includes('github.com/trycua')) {
18 | posthog.capture('github_link_clicked', {
19 | url: href,
20 | link_text: text,
21 | page: window.location.pathname,
22 | });
23 | }
24 |
25 | if (href.includes('discord.com/invite') || href.includes('discord.gg')) {
26 | posthog.capture('discord_link_clicked', {
27 | url: href,
28 | link_text: text,
29 | page: window.location.pathname,
30 | });
31 | }
32 |
33 | if (
34 | (href.includes('trycua.com') && !href.includes('trycua.com/docs')) ||
35 | href.includes('cua.ai')
36 | ) {
37 | posthog.capture('main_website_clicked', {
38 | url: href,
39 | link_text: text,
40 | page: window.location.pathname,
41 | });
42 | }
43 |
44 | if (link.hostname && link.hostname !== window.location.hostname) {
45 | if (
46 | href.includes('github.com/trycua') ||
47 | href.includes('discord.com') ||
48 | href.includes('trycua.com') ||
49 | href.includes('cua.ai')
50 | ) {
51 | return;
52 | }
53 |
54 | posthog.capture('external_link_clicked', {
55 | url: href,
56 | link_text: text,
57 | page: window.location.pathname,
58 | domain: link.hostname,
59 | });
60 | }
61 | };
62 |
63 | document.addEventListener('click', handleClick);
64 |
65 | return () => {
66 | document.removeEventListener('click', handleClick);
67 | };
68 | }, []);
69 |
70 | return null;
71 | }
72 |
```
--------------------------------------------------------------------------------
/libs/python/computer-server/pyproject.toml:
--------------------------------------------------------------------------------
```toml
1 | [build-system]
2 | requires = ["pdm-backend"]
3 | build-backend = "pdm.backend"
4 |
5 | [project]
6 | name = "cua-computer-server"
7 | version = "0.1.31"
8 |
9 | description = "Server component for the Computer-Use Interface (CUI) framework powering Cua"
10 | authors = [
11 | { name = "TryCua", email = "[email protected]" }
12 | ]
13 | readme = "README.md"
14 | license = { text = "MIT" }
15 | requires-python = ">=3.12,<3.14"
16 | dependencies = [
17 | "fastapi>=0.111.0",
18 | "uvicorn[standard]>=0.27.0",
19 | "pydantic>=2.0.0",
20 | "pyautogui>=0.9.54",
21 | "pynput>=1.8.1",
22 | "pillow>=10.2.0",
23 | "aiohttp>=3.9.1",
24 | "pyperclip>=1.9.0",
25 | "websockets>=12.0",
26 | "pywinctl>=0.4.1",
27 | "playwright>=1.40.0",
28 | # OS-specific runtime deps
29 | "pyobjc-framework-Cocoa>=10.1; sys_platform == 'darwin'",
30 | "pyobjc-framework-Quartz>=10.1; sys_platform == 'darwin'",
31 | "pyobjc-framework-ApplicationServices>=10.1; sys_platform == 'darwin'",
32 | "python-xlib>=0.33; sys_platform == 'linux'",
33 | "pywin32>=310; sys_platform == 'win32'",
34 | "python-certifi-win32; sys_platform == 'win32'",
35 | ]
36 |
37 | [project.optional-dependencies]
38 | macos = [
39 | "pyobjc-framework-Cocoa>=10.1",
40 | "pyobjc-framework-Quartz>=10.1",
41 | "pyobjc-framework-ApplicationServices>=10.1"
42 | ]
43 | linux = [
44 | "python-xlib>=0.33"
45 | ]
46 | windows = [
47 | "pywin32>=310"
48 | ]
49 |
50 | [project.urls]
51 | homepage = "https://github.com/trycua/cua"
52 | repository = "https://github.com/trycua/cua"
53 |
54 | [project.scripts]
55 | cua-computer-server = "computer_server:run_cli"
56 |
57 | [tool.pdm]
58 | distribution = true
59 |
60 | [tool.pdm.build]
61 | includes = ["computer_server"]
62 | package-data = {"computer_server" = ["py.typed"]}
63 |
64 | [tool.pdm.dev-dependencies]
65 | test = [
66 | "pytest>=7.0.0",
67 | "pytest-asyncio>=0.23.0"
68 | ]
69 | format = [
70 | "black>=23.0.0",
71 | "isort>=5.12.0"
72 | ]
73 | dev = [
74 | "ruff>=0.0.241",
75 | "mypy>=0.971"
76 | ]
77 |
78 | [tool.pdm.scripts]
79 | api = "python -m computer_server"
```
--------------------------------------------------------------------------------
/libs/lume/src/Commands/Create.swift:
--------------------------------------------------------------------------------
```swift
1 | import ArgumentParser
2 | import Foundation
3 | import Virtualization
4 |
5 | // MARK: - Create Command
6 |
7 | struct Create: AsyncParsableCommand {
8 | static let configuration = CommandConfiguration(
9 | abstract: "Create a new virtual machine"
10 | )
11 |
12 | @Argument(help: "Name for the virtual machine")
13 | var name: String
14 |
15 | @Option(
16 | help: "Operating system to install. Defaults to macOS.",
17 | completion: .list(["macOS", "linux"]))
18 | var os: String = "macOS"
19 |
20 | @Option(help: "Number of CPU cores", transform: { Int($0) ?? 4 })
21 | var cpu: Int = 4
22 |
23 | @Option(
24 | help: "Memory size, e.g., 8192MB or 8GB. Defaults to 8GB.", transform: { try parseSize($0) }
25 | )
26 | var memory: UInt64 = 8 * 1024 * 1024 * 1024
27 |
28 | @Option(
29 | help: "Disk size, e.g., 20480MB or 20GB. Defaults to 50GB.",
30 | transform: { try parseSize($0) })
31 | var diskSize: UInt64 = 50 * 1024 * 1024 * 1024
32 |
33 | @Option(help: "Display resolution in format WIDTHxHEIGHT. Defaults to 1024x768.")
34 | var display: VMDisplayResolution = VMDisplayResolution(string: "1024x768")!
35 |
36 | @Option(
37 | help:
38 | "Path to macOS restore image (IPSW), or 'latest' to download the latest supported version. Required for macOS VMs.",
39 | completion: .file(extensions: ["ipsw"])
40 | )
41 | var ipsw: String?
42 |
43 | @Option(name: .customLong("storage"), help: "VM storage location to use or direct path to VM location")
44 | var storage: String?
45 |
46 | init() {
47 | }
48 |
49 | @MainActor
50 | func run() async throws {
51 | let controller = LumeController()
52 | try await controller.create(
53 | name: name,
54 | os: os,
55 | diskSize: diskSize,
56 | cpuCount: cpu,
57 | memorySize: memory,
58 | display: display.string,
59 | ipsw: ipsw,
60 | storage: storage
61 | )
62 | }
63 | }
64 |
```
--------------------------------------------------------------------------------
/libs/python/mcp-server/scripts/install_mcp_server.sh:
--------------------------------------------------------------------------------
```bash
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | # Create the ~/.cua directory if it doesn't exist
6 | mkdir -p "$HOME/.cua"
7 |
8 | # Create start_mcp_server.sh script in ~/.cua directory
9 | cat > "$HOME/.cua/start_mcp_server.sh" << 'EOF'
10 | #!/bin/bash
11 |
12 | set -e
13 |
14 | # Function to check if a directory is writable
15 | is_writable() {
16 | [ -w "$1" ]
17 | }
18 |
19 | # Function to check if a command exists (silent)
20 | command_exists() {
21 | command -v "$1" >/dev/null 2>&1
22 | }
23 |
24 | # Find a writable directory for the virtual environment
25 | if is_writable "$HOME"; then
26 | VENV_DIR="$HOME/.cua-mcp-venv"
27 | elif is_writable "/tmp"; then
28 | VENV_DIR="/tmp/.cua-mcp-venv"
29 | else
30 | # Try to create a directory in the current working directory
31 | TEMP_DIR="$(pwd)/.cua-mcp-venv"
32 | if is_writable "$(pwd)"; then
33 | VENV_DIR="$TEMP_DIR"
34 | else
35 | echo "Error: Cannot find a writable directory for the virtual environment." >&2
36 | exit 1
37 | fi
38 | fi
39 |
40 | # Check if Python is installed
41 | if ! command_exists python3; then
42 | echo "Error: Python 3 is not installed." >&2
43 | exit 1
44 | fi
45 |
46 | # Check if pip is installed
47 | if ! command_exists pip3; then
48 | echo "Error: pip3 is not installed." >&2
49 | exit 1
50 | fi
51 |
52 | # Create virtual environment if it doesn't exist
53 | if [ ! -d "$VENV_DIR" ]; then
54 | # Redirect output to prevent JSON parsing errors in Claude
55 | python3 -m venv "$VENV_DIR" >/dev/null 2>&1
56 | fi
57 |
58 | # Activate virtual environment
59 | source "$VENV_DIR/bin/activate"
60 |
61 | # Always install/upgrade the latest version of cua-mcp-server
62 | pip install --upgrade "cua-mcp-server"
63 |
64 | # Run the MCP server with isolation from development paths
65 | cd "$VENV_DIR" # Change to venv directory to avoid current directory in path
66 |
67 | python3 -c "from mcp_server.server import main; main()"
68 | EOF
69 |
70 | # Make the script executable
71 | chmod +x "$HOME/.cua/start_mcp_server.sh"
72 |
73 | echo "MCP server startup script created at $HOME/.cua/start_mcp_server.sh"
74 |
```
--------------------------------------------------------------------------------
/docs/src/components/page-feedback.tsx:
--------------------------------------------------------------------------------
```typescript
1 | 'use client';
2 |
3 | import { useState } from 'react';
4 | import posthog from 'posthog-js';
5 | import { ThumbsUp, ThumbsDown } from 'lucide-react';
6 |
7 | export function PageFeedback() {
8 | const [feedback, setFeedback] = useState<'helpful' | 'not_helpful' | null>(null);
9 |
10 | const handleFeedback = (isHelpful: boolean) => {
11 | const feedbackType = isHelpful ? 'helpful' : 'not_helpful';
12 | setFeedback(feedbackType);
13 |
14 | posthog.capture(`page_feedback_${feedbackType}`, {
15 | page: window.location.pathname,
16 | page_title: document.title,
17 | });
18 | };
19 |
20 | return (
21 | <div className="mt-8 pt-4 border-t border-fd-border">
22 | {feedback === null ? (
23 | <div className="flex flex-col sm:flex-row items-center justify-between gap-3">
24 | <p className="text-sm text-fd-muted-foreground">Was this page helpful?</p>
25 | <div className="flex gap-2">
26 | <button
27 | onClick={() => handleFeedback(true)}
28 | className="flex items-center gap-1.5 px-3 py-1.5 text-sm hover:bg-fd-accent rounded transition-colors"
29 | aria-label="This page was helpful"
30 | >
31 | <ThumbsUp className="w-4 h-4" />
32 | Yes
33 | </button>
34 | <button
35 | onClick={() => handleFeedback(false)}
36 | className="flex items-center gap-1.5 px-3 py-1.5 text-sm hover:bg-fd-accent rounded transition-colors"
37 | aria-label="This page was not helpful"
38 | >
39 | <ThumbsDown className="w-4 h-4" />
40 | No
41 | </button>
42 | </div>
43 | </div>
44 | ) : (
45 | <p className="text-sm text-fd-muted-foreground text-left">
46 | {feedback === 'helpful'
47 | ? 'Thanks for your feedback!'
48 | : "Thanks for your feedback. We'll work on improving this page."}
49 | </p>
50 | )}
51 | </div>
52 | );
53 | }
54 |
```
--------------------------------------------------------------------------------
/libs/python/computer/computer/interface/factory.py:
--------------------------------------------------------------------------------
```python
1 | """Factory for creating computer interfaces."""
2 |
3 | from typing import Literal, Optional
4 |
5 | from .base import BaseComputerInterface
6 |
7 |
8 | class InterfaceFactory:
9 | """Factory for creating OS-specific computer interfaces."""
10 |
11 | @staticmethod
12 | def create_interface_for_os(
13 | os: Literal["macos", "linux", "windows"],
14 | ip_address: str,
15 | api_port: Optional[int] = None,
16 | api_key: Optional[str] = None,
17 | vm_name: Optional[str] = None,
18 | ) -> BaseComputerInterface:
19 | """Create an interface for the specified OS.
20 |
21 | Args:
22 | os: Operating system type ('macos', 'linux', or 'windows')
23 | ip_address: IP address of the computer to control
24 | api_port: Optional API port of the computer to control
25 | api_key: Optional API key for cloud authentication
26 | vm_name: Optional VM name for cloud authentication
27 |
28 | Returns:
29 | BaseComputerInterface: The appropriate interface for the OS
30 |
31 | Raises:
32 | ValueError: If the OS type is not supported
33 | """
34 | # Import implementations here to avoid circular imports
35 | from .linux import LinuxComputerInterface
36 | from .macos import MacOSComputerInterface
37 | from .windows import WindowsComputerInterface
38 |
39 | if os == "macos":
40 | return MacOSComputerInterface(
41 | ip_address, api_key=api_key, vm_name=vm_name, api_port=api_port
42 | )
43 | elif os == "linux":
44 | return LinuxComputerInterface(
45 | ip_address, api_key=api_key, vm_name=vm_name, api_port=api_port
46 | )
47 | elif os == "windows":
48 | return WindowsComputerInterface(
49 | ip_address, api_key=api_key, vm_name=vm_name, api_port=api_port
50 | )
51 | else:
52 | raise ValueError(f"Unsupported OS type: {os}")
53 |
```
--------------------------------------------------------------------------------
/libs/python/agent/agent/decorators.py:
--------------------------------------------------------------------------------
```python
1 | """
2 | Decorators for agent - agent_loop decorator
3 | """
4 |
5 | from typing import List, Optional
6 |
7 | from .types import AgentConfigInfo
8 |
9 | # Global registry
10 | _agent_configs: List[AgentConfigInfo] = []
11 |
12 |
13 | def register_agent(models: str, priority: int = 0):
14 | """
15 | Decorator to register an AsyncAgentConfig class.
16 |
17 | Args:
18 | models: Regex pattern to match supported models
19 | priority: Priority for agent selection (higher = more priority)
20 | """
21 |
22 | def decorator(agent_class: type):
23 | # Validate that the class implements AsyncAgentConfig protocol
24 | if not hasattr(agent_class, "predict_step"):
25 | raise ValueError(
26 | f"Agent class {agent_class.__name__} must implement predict_step method"
27 | )
28 | if not hasattr(agent_class, "predict_click"):
29 | raise ValueError(
30 | f"Agent class {agent_class.__name__} must implement predict_click method"
31 | )
32 | if not hasattr(agent_class, "get_capabilities"):
33 | raise ValueError(
34 | f"Agent class {agent_class.__name__} must implement get_capabilities method"
35 | )
36 |
37 | # Register the agent config
38 | config_info = AgentConfigInfo(
39 | agent_class=agent_class, models_regex=models, priority=priority
40 | )
41 | _agent_configs.append(config_info)
42 |
43 | # Sort by priority (highest first)
44 | _agent_configs.sort(key=lambda x: x.priority, reverse=True)
45 |
46 | return agent_class
47 |
48 | return decorator
49 |
50 |
51 | def get_agent_configs() -> List[AgentConfigInfo]:
52 | """Get all registered agent configs"""
53 | return _agent_configs.copy()
54 |
55 |
56 | def find_agent_config(model: str) -> Optional[AgentConfigInfo]:
57 | """Find the best matching agent config for a model"""
58 | for config_info in _agent_configs:
59 | if config_info.matches_model(model):
60 | return config_info
61 | return None
62 |
```
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM python:3.12-slim
2 |
3 | # Set environment variables
4 | ENV PYTHONUNBUFFERED=1 \
5 | PYTHONDONTWRITEBYTECODE=1 \
6 | PIP_NO_CACHE_DIR=1 \
7 | PIP_DISABLE_PIP_VERSION_CHECK=1 \
8 | PYTHONPATH="/app/libs/python/core:/app/libs/python/computer:/app/libs/python/agent:/app/libs/python/som:/app/libs/python/computer-server:/app/libs/python/mcp-server"
9 |
10 | # Install system dependencies for ARM architecture
11 | RUN apt-get update && apt-get install -y --no-install-recommends \
12 | git \
13 | build-essential \
14 | libgl1-mesa-glx \
15 | libglib2.0-0 \
16 | libxcb-xinerama0 \
17 | libxkbcommon-x11-0 \
18 | cmake \
19 | pkg-config \
20 | curl \
21 | iputils-ping \
22 | net-tools \
23 | sed \
24 | xxd \
25 | && apt-get clean \
26 | && rm -rf /var/lib/apt/lists/*
27 |
28 | # Set working directory
29 | WORKDIR /app
30 |
31 | # Copy the entire project temporarily
32 | # We'll mount the real source code over this at runtime
33 | COPY . /app/
34 |
35 | # Create a simple .env.local file for build.sh
36 | RUN echo "PYTHON_BIN=python" > /app/.env.local
37 |
38 | # Modify build.sh to skip virtual environment creation
39 | RUN sed -i 's/python -m venv .venv/echo "Skipping venv creation in Docker"/' /app/scripts/build.sh && \
40 | sed -i 's/source .venv\/bin\/activate/echo "Skipping venv activation in Docker"/' /app/scripts/build.sh && \
41 | sed -i 's/find . -type d -name ".venv" -exec rm -rf {} +/echo "Skipping .venv removal in Docker"/' /app/scripts/build.sh && \
42 | chmod +x /app/scripts/build.sh
43 |
44 | # Run the build script to install dependencies
45 | RUN cd /app && ./scripts/build.sh
46 |
47 | # Clean up the source files now that dependencies are installed
48 | # When we run the container, we'll mount the actual source code
49 | RUN rm -rf /app/* /app/.??*
50 |
51 | # Note: This Docker image doesn't contain the lume executable (macOS-specific)
52 | # Instead, it relies on connecting to a lume server running on the host machine
53 | # via host.docker.internal:7777
54 |
55 | # Default command
56 | CMD ["bash"]
```
--------------------------------------------------------------------------------
/docs/content/docs/agent-sdk/supported-agents/human-in-the-loop.mdx:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | title: Human-In-The-Loop
3 | description: Use humans as agents for evaluation, demonstrations, and interactive control
4 | ---
5 |
6 | The Agent SDK provides a human tool, with native support for using a human-in-the-loop as a way to evaluate your environment, tools, or to create demonstrations. You can use it by doing `grounding_model+human/human` or `human/human` directly.
7 |
8 | ## Getting Started
9 |
10 | To start the human agent tool, simply run:
11 |
12 | ```bash
13 | python -m agent.human_tool
14 | ```
15 |
16 | The UI will show you pending completions. Select a completion to take control of the agent.
17 |
18 | ## Usage Examples
19 |
20 | ### Direct Human Agent
21 |
22 | ```python
23 | from agent import ComputerAgent
24 | from agent.computer import computer
25 |
26 | agent = ComputerAgent(
27 | "human/human",
28 | tools=[computer]
29 | )
30 |
31 | async for _ in agent.run("Take a screenshot, analyze the UI, and click on the most prominent button"):
32 | pass
33 | ```
34 |
35 | ### Composed with Grounding Model
36 |
37 | ```python
38 | agent = ComputerAgent(
39 | "huggingface-local/HelloKKMe/GTA1-7B+human/human",
40 | tools=[computer]
41 | )
42 |
43 | async for _ in agent.run("Navigate to the settings page and enable dark mode"):
44 | pass
45 | ```
46 |
47 | ## Features
48 |
49 | The human-in-the-loop interface provides:
50 |
51 | - **Interactive UI**: Web-based interface for reviewing and responding to agent requests
52 | - **Image Display**: Screenshots with click handlers for direct interaction
53 | - **Action Accordions**: Support for various computer actions (click, type, keypress, etc.)
54 | - **Tool Calls**: Full OpenAI-compatible tool call support
55 | - **Real-time Updates**: Smart polling for responsive UI updates
56 |
57 | ## Use Cases
58 |
59 | - **Evaluation**: Have humans evaluate agent performance and provide ground truth responses
60 | - **Demonstrations**: Create training data by having humans demonstrate tasks
61 | - **Interactive Control**: Take manual control when automated agents need human guidance
62 | - **Testing**: Validate agent, tool, and environment behavior manually
63 |
64 | ---
65 |
```
--------------------------------------------------------------------------------
/tests/test_telemetry.py:
--------------------------------------------------------------------------------
```python
1 | """
2 | Required environment variables:
3 | - CUA_API_KEY: API key for Cua cloud provider
4 | """
5 |
6 | import os
7 | import sys
8 | from pathlib import Path
9 |
10 | import pytest
11 |
12 | # Load environment variables from .env file
13 | project_root = Path(__file__).parent.parent
14 | env_file = project_root / ".env"
15 | print(f"Loading environment from: {env_file}")
16 | from dotenv import load_dotenv
17 |
18 | load_dotenv(env_file)
19 |
20 | # Add paths to sys.path if needed
21 | pythonpath = os.environ.get("PYTHONPATH", "")
22 | for path in pythonpath.split(":"):
23 | if path and path not in sys.path:
24 | sys.path.insert(0, path) # Insert at beginning to prioritize
25 | print(f"Added to sys.path: {path}")
26 |
27 | from core.telemetry import destroy_telemetry_client, is_telemetry_enabled, record_event
28 |
29 |
30 | class TestTelemetry:
31 | def setup_method(self):
32 | """Reset environment variables before each test"""
33 | os.environ.pop("CUA_TELEMETRY", None)
34 | os.environ.pop("CUA_TELEMETRY_ENABLED", None)
35 | destroy_telemetry_client()
36 |
37 | def test_telemetry_disabled_when_cua_telemetry_is_off(self):
38 | """Should return false when CUA_TELEMETRY is off"""
39 | os.environ["CUA_TELEMETRY"] = "off"
40 | assert is_telemetry_enabled() is False
41 |
42 | def test_telemetry_enabled_when_cua_telemetry_not_set(self):
43 | """Should return true when CUA_TELEMETRY is not set"""
44 | assert is_telemetry_enabled() is True
45 |
46 | def test_telemetry_disabled_when_cua_telemetry_enabled_is_0(self):
47 | """Should return false if CUA_TELEMETRY_ENABLED is 0"""
48 | os.environ["CUA_TELEMETRY_ENABLED"] = "0"
49 | assert is_telemetry_enabled() is False
50 |
51 | def test_send_test_event_to_posthog(self):
52 | """Should send a test event to PostHog"""
53 | # This should not raise an exception
54 | record_event("test_telemetry", {"message": "Hello, world!"})
55 |
56 |
57 | if __name__ == "__main__":
58 | # Run tests directly
59 | pytest.main([__file__, "-v"])
60 |
```
--------------------------------------------------------------------------------
/libs/lume/src/Utils/Utils.swift:
--------------------------------------------------------------------------------
```swift
1 | import Foundation
2 | import ArgumentParser
3 |
4 | extension Collection {
5 | subscript (safe index: Index) -> Element? {
6 | indices.contains(index) ? self[index] : nil
7 | }
8 | }
9 |
10 | func resolveBinaryPath(_ name: String) -> URL? {
11 | guard let path = ProcessInfo.processInfo.environment["PATH"] else {
12 | return nil
13 | }
14 |
15 | for pathComponent in path.split(separator: ":") {
16 | let url = URL(fileURLWithPath: String(pathComponent))
17 | .appendingPathComponent(name, isDirectory: false)
18 |
19 | if FileManager.default.fileExists(atPath: url.path) {
20 | return url
21 | }
22 | }
23 |
24 | return nil
25 | }
26 |
27 | // Helper function to parse size strings
28 | func parseSize(_ input: String) throws -> UInt64 {
29 | let lowercased = input.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
30 | let multiplier: Double
31 | let valueString: String
32 |
33 | if lowercased.hasSuffix("tb") {
34 | multiplier = 1024 * 1024 * 1024 * 1024
35 | valueString = String(lowercased.dropLast(2))
36 | } else if lowercased.hasSuffix("gb") {
37 | multiplier = 1024 * 1024 * 1024
38 | valueString = String(lowercased.dropLast(2))
39 | } else if lowercased.hasSuffix("mb") {
40 | multiplier = 1024 * 1024
41 | valueString = String(lowercased.dropLast(2))
42 | } else if lowercased.hasSuffix("kb") {
43 | multiplier = 1024
44 | valueString = String(lowercased.dropLast(2))
45 | } else {
46 | multiplier = 1024 * 1024
47 | valueString = lowercased
48 | }
49 |
50 | guard let value = Double(valueString.trimmingCharacters(in: .whitespacesAndNewlines)) else {
51 | throw ValidationError("Malformed size input: \(input). Could not parse numeric value.")
52 | }
53 |
54 | let bytesAsDouble = (value * multiplier).rounded()
55 |
56 | guard bytesAsDouble >= 0 && bytesAsDouble <= Double(UInt64.max) else {
57 | throw ValidationError("Calculated size out of bounds for UInt64: \(input)")
58 | }
59 |
60 | let val = UInt64(bytesAsDouble)
61 |
62 | return val
63 | }
64 |
```
--------------------------------------------------------------------------------
/libs/python/agent/agent/callbacks/budget_manager.py:
--------------------------------------------------------------------------------
```python
1 | from typing import Any, Dict, List
2 |
3 | from .base import AsyncCallbackHandler
4 |
5 |
6 | class BudgetExceededError(Exception):
7 | """Exception raised when budget is exceeded."""
8 |
9 | pass
10 |
11 |
12 | class BudgetManagerCallback(AsyncCallbackHandler):
13 | """Budget manager callback that tracks usage costs and can stop execution when budget is exceeded."""
14 |
15 | def __init__(
16 | self, max_budget: float, reset_after_each_run: bool = True, raise_error: bool = False
17 | ):
18 | """
19 | Initialize BudgetManagerCallback.
20 |
21 | Args:
22 | max_budget: Maximum budget allowed
23 | reset_after_each_run: Whether to reset budget after each run
24 | raise_error: Whether to raise an error when budget is exceeded
25 | """
26 | self.max_budget = max_budget
27 | self.reset_after_each_run = reset_after_each_run
28 | self.raise_error = raise_error
29 | self.total_cost = 0.0
30 |
31 | async def on_run_start(self, kwargs: Dict[str, Any], old_items: List[Dict[str, Any]]) -> None:
32 | """Reset budget if configured to do so."""
33 | if self.reset_after_each_run:
34 | self.total_cost = 0.0
35 |
36 | async def on_usage(self, usage: Dict[str, Any]) -> None:
37 | """Track usage costs."""
38 | if "response_cost" in usage:
39 | self.total_cost += usage["response_cost"]
40 |
41 | async def on_run_continue(
42 | self,
43 | kwargs: Dict[str, Any],
44 | old_items: List[Dict[str, Any]],
45 | new_items: List[Dict[str, Any]],
46 | ) -> bool:
47 | """Check if budget allows continuation."""
48 | if self.total_cost >= self.max_budget:
49 | if self.raise_error:
50 | raise BudgetExceededError(
51 | f"Budget exceeded: ${self.total_cost} >= ${self.max_budget}"
52 | )
53 | else:
54 | print(f"Budget exceeded: ${self.total_cost} >= ${self.max_budget}")
55 | return False
56 | return True
57 |
```