feat(core): migrate Computer Use to cua-driver (cross-platform)#5051
Conversation
Replace the open-computer-use (ocu) npm backend with cua-driver-rs, the Rust driver from trycua/cua — a background, no-focus-stealing native automation driver speaking MCP over stdio. Computer Use is experimental; this is a clean replace with no backend flag. Distribution - Download the pinned, signed + notarized binary into ~/.qwen/computer-use/ (OSS mirror primary, GitHub release fallback, QWEN_COMPUTER_USE_DOWNLOAD_HOST override), sha256-verified against the release checksums.txt before extraction. Per-platform install - macOS: tar.gz carrying CuaDriver.app; spawn the in-bundle binary so cua-driver's TCC auto-relaunch attributes Accessibility / Screen Recording to com.trycua.driver instead of the launching terminal. The first-use flow grants ONE permission at a time via a no-gate status daemon polled every 5s. - Linux: bare-binary tarball (lone cua-driver at the archive root). - Windows: .zip extracted via OS tools (bsdtar, then PowerShell Expand-Archive) — no new dependency. Runtime - Expose the full 35-tool cua-driver surface (no curation). - Transparent reconnect + retry on daemon restart / transport close (the Screen-Recording-grant restart that broke first use). CI - Add a three-OS (windows/ubuntu/macos) download+extract smoke job — the only real coverage of the Linux/Windows download paths, which can't run on the macOS dev box.
5f1681a to
1033839
Compare
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
wenshao
left a comment
There was a problem hiding this comment.
Additional findings on files not in this diff (or unchanged code):
[Critical] Destructive tools auto-approved in AUTO_EDIT mode. All 35 computer-use tools use type: 'info', which isAutoEditApproved() auto-approves in AUTO_EDIT mode. This silently approves kill_app (force-kill any PID), page with execute_javascript (run arbitrary JS in any browser — can exfiltrate cookies/passwords), and launch_app with CDP ports (remote code execution).
Fix: Override getConfirmationDetails() to return type: 'edit' for destructive tools (kill_app, page/execute_javascript, launch_app with debugging ports).
— qwen3.7-max via Qwen Code /review
-
[Critical] tool-names.ts:57 — Stale tool name constants
COMPUTER_USE_GET_APP_STATEandCOMPUTER_USE_PERFORM_SECONDARY_ACTIONremain but the tools were renamed/removed. 26 new tools have no entries. Regenerate fromCOMPUTER_USE_TOOL_NAMESin schemas.ts. -
[Critical] settingsSchema.ts:1983 — Computer Use settings description still says "open-computer-use MCP server", lists old 9 tool names, says "fetched via npx". Update to reflect cua-driver, 35 tools, binary download.
-
[Suggestion] vscode-ide-companion/schemas/settings.schema.json:912 — Same stale descriptions as settingsSchema.ts (not touched by this PR but will ship outdated info to VSCode extension users).
— qwen3.7-max via Qwen Code /review
d284979 to
e5e711c
Compare
wenshao
left a comment
There was a problem hiding this comment.
Reviewed the full diff plus the surrounding contracts on the PR head (index.ts registration, install-state.ts, the sync scripts, root vitest config). Direction and engineering quality are good: honest verification table, checksum-before-extract with atomic staging+rename install, version-encoded approval key forcing re-approval on bump, lazy/deferred registration keeping the 35 tools out of the base prompt, and the structuredContent forwarding fixes a real model-blindness bug (list_windows only says "Found N window(s)" in text). The reconnect work targets the real SR-grant restart failure mode, and the OSS sync workflow verifies checksums both before upload and after via read-back.
Blocking-level items (details inline):
- Supply chain:
checksums.txtis fetched from the same first-reachable source as the asset itself (mirror-first), so the integrity check doesn't protect against a compromised mirror — suggest pinning the per-asset sha256s inconstants.ts(downloader.ts). scripts/sync-computer-use-schemas.tswas not migrated — it still spawns the old npm package and asserts exactly 9 tools, so the documented bump path is broken (constants.ts).- Download concurrency: no in-flight dedup, and the tmp dir's per-home scoping is a no-op (it hashes only the first 4 path chars), including cross-user EACCES on shared-
/tmpLinux (downloader.ts).
Strongly suggested: the ~5s cold-start regression for already-granted macOS users plus non-abort-aware sleep (bootstrap.ts), pkill scope that kills daemons qwen-code doesn't own (bootstrap.ts), unknown → immediate kill/relaunch fighting Gatekeeper's first-launch check (bootstrap.ts), the full 35-tool surface × AUTO_EDIT/AUTO/YOLO auto-approval now covering kill_app / page.execute_javascript / start_recording (schemas.test.ts), and transport-error regex false positives with 3x retry amplification for mutating calls (client.ts).
Minor, no inline anchor: index.ts registerComputerUseTools doc comment still says "Register all 9 computer-use tools" (now the full 35-tool surface).
Draft status looks right, and using a CI smoke job as the first real exercise of the win/linux download paths is a sound strategy.
wenshao
left a comment
There was a problem hiding this comment.
Two cross-file staleness issues live in files outside this diff, so they can't be inline-anchored:
- [Suggestion]
packages/cli/src/config/settingsSchema.ts:1983,1993— thetools.computerUsesetting description still documents the removed backend: "via the upstream open-computer-use MCP server", the old 9-tool list (get_app_state,perform_secondary_action), and "fetched via npx". All of it is false after this PR, and this is the text users consent to when enabling the feature. - [Suggestion]
packages/core/src/tools/tool-names.ts:56-65,107-116—ToolNames/ToolDisplayNamesstill enumerate the old 9 ocu tool names (incl.computer_use__get_app_stateandcomputer_use__perform_secondary_action, both gone from the new 35-tool surface); registration only compiles via theas ToolNamecast incomputer-use/index.ts. The canonical name registry now contains dead names and misses the 28 real ones.
— claude-fable-5 via Claude Code /qreview
Point OSS_MIRROR_BASE at the real shared bucket (qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/computer-use). The prior placeholder host (qwen-code.oss-...) never existed, so the primary source always failed and every download fell back to GitHub. Add .github/workflows/sync-cua-driver-to-oss.yml: mirrors the per-platform cua-driver-rs binaries + checksums.txt from the upstream trycua/cua release onto OSS under computer-use/cua-driver-rs/v<ver>/. It AUTO-TRIGGERS on pushes to main that touch constants.ts, so a CUA_DRIVER_VERSION bump mirrors the new release without anyone remembering to; a checksums.txt guard makes it a no-op when the version is already mirrored, and workflow_dispatch + force allow manual / first-time / re-mirror runs. Version is read from constants.ts (no drift). sha256 is verified before upload and re-fetched + verified from OSS after. Reuses the existing production-release OSS credentials + ossutil + upload-aliyun-oss-assets.js. Until a version is mirrored, the GitHub fallback serves it transparently.
e5e711c to
0f6eea9
Compare
|
Thanks for the PR! This is a well-engineered migration. Template: The body doesn't follow the PR template — it uses Direction: ✅ Aligned. Computer Use is an established feature, and migrating from Approach: Scope feels right. The ~900 lines of hardcoded schemas are unavoidable (generated from upstream Original blockers — all resolved across 3 review rounds:
Remaining minor findings (non-blocking):
Moving on to code review. 🔍 中文说明感谢贡献!这是一个工程质量很高的迁移 PR。 模板: PR body 没有按照 PR 模板 的格式。内容充实但建议重新整理。(不阻塞。) 方向: ✅ 对齐。从 方案: 范围合理。 原始阻塞问题——全部在 3 轮 review 中解决:
剩余次要发现(不阻塞):
进入代码审查 🔍 — Qwen Code · qwen3.7-max |
The three-OS computer_use_download_smoke job + downloader.smoke.test.ts served their one-off purpose: confirming per-platform download+extract works on real windows/ubuntu/macos runners, which caught + verified fixes for two Windows-only bugs (.part rejected by Expand-Archive; bsdtar reading C:\ as a remote host). With that validated, drop the job to avoid a real-download run + external-network flakiness on every PR. downloader.test.ts retains unit coverage of the Windows zip path via an injected unzip; the sync workflow still verifies OSS downloads on version bump.
Code Review (re-run on latest head
|
|
Stepping back to look at the whole picture after the re-run. This PR has been through three rounds of review since the initial triage, and every blocker has been addressed with care:
The testing confirms everything works: 113 unit tests pass, typecheck is clean across all packages, and the tmux test on Linux shows the full bootstrap path delivering real results (187 processes). The maintainer's independent macOS verification (build from source, real download, sha256 verify, live MCP handshake, 35==35 tool surface match) adds strong confidence. My independent proposal was: swap backend, download+verify binary, spawn via stdio, handle TCC, update schemas. The PR matches and exceeds this with the OSS mirror, CI smoke validation, and a well-designed reconnect + permission flow. Two minor follow-ups (not blocking):
This is ready to ship. Approving. ✅ 中文说明在重新运行后从整体来看这个 PR。 经过三轮 review,所有阻塞问题都已解决:
测试验证一切正常:113 个单元测试通过,类型检查干净,tmux 测试在 Linux 上展示了完整的引导路径(187 个进程)。维护者的独立 macOS 验证增加了强有力的信心。 两个次要后续(不阻塞):
可以合并了。批准。✅ — Qwen Code · qwen3.7-max |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Needs three fixes before merge — supply chain integrity (pin sha256s or fetch checksums from GitHub only), AUTO_EDIT safety (override confirmation type for destructive tools like kill_app/page/start_recording), and stale references outside the diff (tool-names.ts, settingsSchema.ts). Engineering quality is high and testing confirms it works — see my notes above. 🙏
…outs - bootstrap: short-circuit a warm client before the install gate + download, so unit tests no longer trigger a real ~20MB download or write install-state into the repo (the started-client check now precedes deps.install()). - tool: autoApproveInstall = !!config. Reaching execute() means the call was already approved (dialog / always-allow rule / auto-approve mode), closing the DEFAULT-mode "install declined" dead-end a saved always-allow rule triggered on every retry. - tool: high-risk calls (kill_app, launch_app, start_recording, set_config, page->execute_javascript) surface as 'mcp' confirmation type so AUTO_EDIT no longer silently auto-approves them; AUTO still routes them through its classifier, YOLO still auto-approves. Install dialog now always shows Args. - downloader: per-URL fetch timeout (AbortSignal.timeout 30s) so a stalled mirror fails over instead of hanging for minutes; Windows unzip tries bsdtar WITHOUT --force-local first (bsdtar rejects the flag — the old order made it always fail and silently lean on PowerShell), then GNU-tar --force-local, then PowerShell; honest supply-chain note (same-source checksum guards transit corruption, not a compromised mirror). - client.test: ReconnectTestClient injects a fake inner client instead of re-implementing a single-retry mirror, so production's 3-attempt loop + backoff + exhaustion + non-transport rethrow are actually covered. - bootstrap.test: cover autoApproveInstall (no prompt + state persisted).
…eview round 1) - tool-names: drop the dead ocu-era computer_use__* block — it still carried get_app_state / perform_secondary_action (gone from the 35-tool surface) and was referenced only via index.ts's `as ToolName` cast. The canonical surface lives in computer-use/schemas.ts; enumerating a subset here only goes stale. - settings: the computerUse description (and the regenerated VSCode schema) — the consent text users read — said "open-computer-use MCP server / fetched via npx / 9 tools"; now reflects cua-driver / signed binary download / the full surface. - index.ts doc: "9 tools" -> full cua-driver surface.
Review round 1 — resolutionThanks for the thorough pass (qwen3.7-max + claude-fable-5). Triaged every finding; fixed the real bugs and the correctness/security items, declined the experimental-feature hardening that's out of scope for this backend migration. Two commits: Fixed (
|
…eout) The two production-loop coverage tests used fake timers; pre-commit eslint --fix added an `await` before the `runAllTimersAsync()` flush, so the rejecting promise was awaited before its backoff timers advanced — the exhaustion test hung and timed out at 5s in CI (it passed locally only by timing luck). Switch both to real timers with generous per-test timeouts (10s / 15s).
…args, download timeout - high-risk gate: add `replay_trajectory` (replays arbitrary recorded actions through the same dispatch path — a confused-deputy bypass of the round-1 gate) and the `page` action `enable_javascript_apple_events` (permanently patches the browser's prefs + relaunches it) to HIGH_RISK_TOOLS / the page predicate. Both resolved to `info` and were AUTO_EDIT-auto-approved before. - high-risk confirmation: the 'mcp' dialog renders only server+tool name, so fold the args into the title — the dangerous calls now show MORE detail than the safe ones (injected JS, --remote-debugging-port, recording dest, replay dir), not less. - download: the per-URL 30s timeout was a hard cap on the WHOLE transfer (the body is consumed later in pipeline), so a ~20MB binary over a slow CN->GitHub link got aborted mid-stream. Apply the 30s only to obtaining the response headers and clear it the moment they arrive; the body download is uncapped. - constants: the OSS-mirror comment said "manual workflow_dispatch on each bump" but the workflow auto-triggers on constants.ts pushes — corrected.
Review round 2 — resolutionAll four findings were real gaps/regressions in the round-1 fixes. Fixed in
Good catches on the round-1 gate's completeness. |
✅ Local real-build verification (maintainer) — recommend mergeBuilt this PR from source in an isolated worktree ( Results
Highlights worth calling out1. Asset mapping matches upstream exactly — including the easy-to-get-wrong asymmetry. Fetched the real
2. Real download + integrity is genuinely exercised. Production 3. Tool surface has zero drift. The live binary's 4. Tests are not vacuous. Reverted two source details while keeping the PR's tests: dropping the Linux Findings (non-blocking)
VerdictThe cross-platform install/download/verify/extract core — the part that can't run on the mac dev box for Linux/Windows, and the riskiest surface in this PR — is correct and robust on macOS arm64, with asset mapping matching upstream for all three OSes, a working fallback, no schema drift, and a comprehensive, non-vacuous test suite. Recommend merge. On merge, the 🇨🇳 中文版(点击展开)✅ 本地真实构建验证(维护者)— 建议合并在隔离 worktree( 结果
值得强调的几点1. 资产映射与上游完全一致——连最容易搞错的不对称都对了。 拉取了 tag
2. 真实下载 + 完整性校验是真跑了的。 生产 3. 工具面零漂移。 真实二进制的 4. 测试不是空过场。 在保留 PR 测试的前提下回退了两处源码:去掉 Linux 的 发现(均不阻塞合并)
结论跨平台的安装/下载/校验/解包核心——也就是 Linux/Windows 那部分无法在 mac 开发机上跑、且是本 PR 风险最高的面——在 macOS arm64 上正确且稳健:三个 OS 的资产映射都与上游一致、回退可用、schema 零漂移、测试套件全面且非空过场。建议合并。 合并后 Verified locally on macOS arm64 by the maintainer via real source build + live binary; full GUI runtime behind macOS TCC grants is out of scope for this automated check. |
…e timeout, gate typing - high-risk confirmation: revert round-2's args-into-mcp-`title` fold. No confirmation surface (TUI / non-interactive / ACP) renders ToolMcpConfirmationDetails.title, so it was dead text. Args already reach the user via the tool-header line (getDescription()); the gate's job is forcing the dialog (mcp type -> not AUTO_EDIT-auto-approved). Also fix the test that vacuously asserted title.contains(args) without checking it was rendered. - download: add a body-level idle watchdog (60s, reset per chunk). Round 2's headers-only timeout is cleared once headers arrive, so a source that sends headers then stalls mid-stream would hang the install forever; the watchdog fails over a stalled body while leaving a slow-but-progressing one alone. - gate: type HIGH_RISK_TOOLS as Set<ComputerUseToolName> + a module-load assertion that every entry is a real tool name — a typo would otherwise compile cleanly and silently disable the gate for that tool.
Review round 3 — resolutionThree findings, all on the round-2 fixes themselves. Fixed in
Thread 1 especially: round 2 claimed the fix without verifying the mcp title is ever rendered — fair catch. |
|
@qwen-code /triage |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. All three original blockers resolved across 3 review rounds. 113/113 tests pass, typecheck clean, tmux real-scenario test confirms full bootstrap path works. ✅
Great work on this migration 🎉This is a solid piece of engineering — cross-platform binary distribution, integrity verification, macOS TCC permission flow, OSS mirror pipeline — the infrastructure around A concern from real-world usageIn my own testing with the current (open-computer-use) driver, I've had good results on tasks like:
Subjectively, Suggestion: keep both drivers behind a flagRather than a clean replace, consider:
This doesn't need to be permanent — once 中文这个 PR 工程量很大 👏跨平台二进制分发、完整性校验、macOS TCC 权限流程、OSS 镜像管线——围绕 一个来自实际使用的顾虑我自己用现有的
体感上 ocu 处理得不错。而 建议:保留双驱动,用开关切换与其做 clean replace,建议:
这不是永久方案——等 |
|
One more thing I really appreciated about From what I can tell, If we go with
中文再补充一点——我很喜欢 据我了解, 如果最终以
|
|
@pomelo-nwu Thanks for the careful read, and especially for the concrete real-world data points (Stocks UI replication, DingTalk contact lookup + messaging) — that kind of first-hand field experience is exactly what's useful here. On the dual-driver suggestion: this PR is intentionally scoped as a clean migration from That said, your Electron / CJK-input concern is fair. The migration stays fully revertible (the complete ocu implementation remains in git history), so the lower-risk sequence is: land the clean migration now, and if 中文感谢细致的 review,尤其是钉钉、Stocks 这些真实场景的数据点 —— 这种一手使用经验很有参考价值。 关于双驱动:这个 PR 的定位就是 不过 Electron / 中文输入法的顾虑确实合理。迁移本身完全可 revert(ocu 的完整实现都还在 git 历史里),所以风险更低的做法是:先合这个干净迁移;如果上线后 |
The cua-driver migration (#5051) left scripts/sync-computer-use-schemas.ts pointing at the old open-computer-use backend: it npx'd @qwen-code/open-computer-use, hard-coded the 9-tool ocu surface, and emitted an "open-computer-use" header. Re-running it — which constants.ts' version-bump procedure tells maintainers to do — would have clobbered the migrated 35-tool cua-driver schemas.ts. - Drive the locally-pinned `cua-driver mcp` binary (binaryPath / CUA_DRIVER_VERSION from constants.ts) instead of npx'ing ocu; expect 35 tools and warn (don't fail) on drift. - Emit the cua-driver-flavored schemas.ts header. - Refresh install-state.test.ts fixtures from ocu package specs to the cua-driver-rs approval-key form the field actually stores now. Verified the fixed script reproduces the committed 35-tool surface exactly (modulo prettier formatting). No dead env-var handling remained — the module reads only QWEN_COMPUTER_USE_{AUTO_APPROVE,DOWNLOAD_HOST,MAX_IMAGE_DIMENSION}.
…env) (#5122) * feat(computer-use): configurable screenshot max dimension (setting + env) Add a user-level knob for cua-driver's screenshot longest-edge cap. The old open-computer-use backend exposed this via OPEN_COMPUTER_USE_IMAGE_* env vars; the cua-driver migration dropped them, leaving only the model-driven set_config tool. This restores deterministic user control. - Setting tools.computerUse.maxImageDimension (number; default -1 = keep cua-driver's built-in default of 1568; 0 disables resizing / full resolution; a positive value caps the longest edge). - Env override QWEN_COMPUTER_USE_MAX_IMAGE_DIMENSION (takes precedence over the setting; invalid/negative values fall through). - Resolution lives in resolveMaxImageDimension(); applied via the cua-driver set_config tool once per (re)connect in ComputerUseClient.doStart — best-effort, never aborts startup, and re-applied after a daemon-restart reconnect. - Docs: document tools.computerUse.{enabled,maxImageDimension} in settings.md (the block was previously undocumented). Refresh stale ocu/npx comments left in client.ts + install-state.ts by the migration. Precedence: env var > setting > cua-driver default. * chore(computer-use): finish ocu→cua-driver cleanup in schema-sync script The cua-driver migration (#5051) left scripts/sync-computer-use-schemas.ts pointing at the old open-computer-use backend: it npx'd @qwen-code/open-computer-use, hard-coded the 9-tool ocu surface, and emitted an "open-computer-use" header. Re-running it — which constants.ts' version-bump procedure tells maintainers to do — would have clobbered the migrated 35-tool cua-driver schemas.ts. - Drive the locally-pinned `cua-driver mcp` binary (binaryPath / CUA_DRIVER_VERSION from constants.ts) instead of npx'ing ocu; expect 35 tools and warn (don't fail) on drift. - Emit the cua-driver-flavored schemas.ts header. - Refresh install-state.test.ts fixtures from ocu package specs to the cua-driver-rs approval-key form the field actually stores now. Verified the fixed script reproduces the committed 35-tool surface exactly (modulo prettier formatting). No dead env-var handling remained — the module reads only QWEN_COMPUTER_USE_{AUTO_APPROVE,DOWNLOAD_HOST,MAX_IMAGE_DIMENSION}.
Migrate the built-in Computer Use tool surface from open-computer-use (npm/npx) to cua-driver-rs (native Rust driver, trycua/cua). - Replace the 9-tool ocu surface with the full 35-tool cua-driver surface (page/CDP, cursor, session, recording, config, app lifecycle, …), generated from the live `cua-driver mcp` tools/list — pinned to v0.5.2. - Per-platform signed + notarized binary distribution: download into ~/.qwen/computer-use/ with SHA-256 integrity verification, a three-tier Windows unzip fallback, headers/idle timeouts, and a bounded retry loop. - macOS TCC permission flow via CuaDriver.app (com.trycua.driver), polled one-at-a-time through the no-gate status daemon. - Mirror cua-driver assets on the qwen-code-assets OSS bucket with a push-to-main sync workflow guarded by checksums. - Gate high-risk tools (kill_app, launch_app, start_recording, set_config, replay_trajectory, page JS execution) to an explicit confirmation type so AUTO_EDIT cannot silently auto-approve them; AUTO defers to the classifier, YOLO auto-approves.
…docs - README.md: add Qwen Code and Droid CLI to the supported clients list - connect-your-agent.mdx: add dedicated sections for Qwen Code and Droid CLI with setup instructions; update page description - install.sh: add Qwen Code and Droid CLI to the post-install MCP hint block - cli.rs: add 'qwen'/'qwen-code' and 'droid'/'droid-cli' to run_mcp_config; update client list in error message and doc comment - mcp_config_parity.rs: add parity test cases for qwen and droid clients Qwen Code (QwenLM/qwen-code) migrated its Computer Use backend to cua-driver in QwenLM/qwen-code#5051. Droid CLI (Factory AI) is an MCP-capable coding agent that supports stdio servers via 'droid mcp add ... --type stdio'.
…docs - README.md: add Qwen Code and Droid CLI to the supported clients list - connect-your-agent.mdx: add dedicated sections for Qwen Code and Droid CLI with setup instructions; update page description - install.sh: add Qwen Code and Droid CLI to the post-install MCP hint block - cli.rs: add 'qwen'/'qwen-code' and 'droid'/'droid-cli' to run_mcp_config; update client list in error message and doc comment - mcp_config_parity.rs: add parity test cases for qwen and droid clients Qwen Code (QwenLM/qwen-code) migrated its Computer Use backend to cua-driver in QwenLM/qwen-code#5051. Droid CLI (Factory AI) is an MCP-capable coding agent that supports stdio servers via 'droid mcp add ... --type stdio'.
What
Migrate the built-in Computer Use tool from the open-computer-use (ocu) npm backend to cua-driver-rs, the Rust driver from trycua/cua — a background, no-focus-stealing native automation driver speaking MCP over stdio.
Computer Use is experimental; this is a clean replace (no backend flag, ocu removed). The full 35-tool surface is exposed (no curation).
How it ships
cua-driver is not on npm — it's per-platform signed+notarized binaries on GitHub releases (tag
cua-driver-rs-v<version>, pinned to 0.5.2). First use downloads the pinned asset into~/.qwen/computer-use/:Source order: qwen-code-assets OSS mirror (primary) -> trycua/cua GitHub release (fallback), with a
QWEN_COMPUTER_USE_DOWNLOAD_HOSToverride.Integrity: sha256 from the release
checksums.txt, verified before extraction.Per-platform layout (verified against upstream
install.sh/_install-rust.sh/install.ps1):darwin-{arm64,x86_64}.tar.gzCuaDriver.app; spawn the in-bundle binarylinux-x86_64-binary.tar.gzcua-driverat the archive rootwindows-{x86_64,arm64}.zipcua-driver.exe; unzip via bsdtar -> PowerShellmacOS permission flow
cua-driver only attributes Accessibility/Screen-Recording TCC grants to
com.trycua.driverwhen the running image is insideCuaDriver.app— so we spawn the in-bundle binary andlsregisterit. First use guides one permission at a time via aserve --no-permissions-gatestatus daemon polled every 5s. Daemon restarts (from the Screen-Recording grant) are absorbed by transparent client reconnect.Linux/Windows runtime — out of scope here, and that is source-confirmed safe
This PR delivers install + download on all three OSes, and runtime on macOS. Linux/Windows runtime (actually driving the GUI) is intentionally not validated here — but that is safe, not a gap, confirmed by reading cua-driver-rs source:
--no-permissions-gateislet _ =-discarded on other platforms (main.rs:573-595),permissions grantexits non-zero (cli.rs:1725), and upstreamPARITY.md: "windows / linux: N/A — TCC is a macOS concept." So skipping the permission flow on non-mac is correct.Resulterror, never a daemon crash.Optional future UX (non-blocking, follow-up): surface
cua-driver doctor --jsonreadiness hints on non-mac (Windows Session 0; LinuxDISPLAY/Wayland/AT-SPI).Verification status
tsc --noEmitBefore merge, a temporary three-OS
computer_use_download_smokeCI job exercised the real download+extract on windows/ubuntu/macos runners — it passed on all three and caught two real Windows bugs (.partextension rejected by Expand-Archive; bsdtar readingC:\as a remote host), both fixed here. With that one-off validation done, the job has been removed to avoid a real-download run + external-network flakiness on every PR.downloader.test.tskeeps unit coverage of the Windows.zippath via an injected unzip.OSS mirror
OSS_MIRROR_BASEpoints at the sharedqwen-code-assetsbucket..github/workflows/sync-cua-driver-to-oss.ymlmirrors the binaries from the trycua/cua release onto OSS, reusing the existingproduction-releaseOSS credentials + ossutil + upload helper. It auto-triggers on pushes to main that touchconstants.ts, so aCUA_DRIVER_VERSIONbump auto-mirrors the new release (achecksums.txtguard no-ops when already mirrored); manualworkflow_dispatch+forcecover first-time / re-mirror runs. Merging this PR (it touchesconstants.ts) triggers the first mirror. Until a version is on OSS, the GitHub fallback serves it transparently.Test plan