qwen-code/packages/cua-driver/scripts/install.sh
顾盼 9605bcc25f
feat(cua-driver): sync vendored cua-driver 0.6.8 → 0.7.0 (#6212)
* feat(cua-driver): sync vendored cua-driver from 0.6.8 to 0.7.0

Upstream 0.7.0 brings action-time modality (get_window_state always
returns AX tree + screenshot, capture_mode retired), honest verification
(every action returns verified/path/effect/escalation), browser page
(CDP) tools, desktop-scope (capture_scope=window|desktop,
get_desktop_state, screen-absolute click/scroll), and Rust-only
consolidation (Swift backend retired).

Cherry-pick patch changes:
- #2025 (X11 synthetic click): retired — superseded by 0.7.0
  honest-verification
- #2035 (session revive): retired — superseded by 0.7.0
  revive_session/loud-reject/is_session_lifecycle_tool
- #2036 (EAGAIN socket write retry): re-injected — merged upstream
  post-0.7.0 tag, EAGAIN os error 35 is not acceptable
- #2021 (null/empty-title windows + resolve_uwp_host_window): retained,
  adapted to 0.7.0's new is_listable_top_level and window_title helpers

Shim extensions:
- coord_norm: desktop-scope support — denormalize_args falls back to
  screen basis when no window cache (desktop-scope click),
  normalize_result handles get_desktop_state, ingest_screen_size reads
  get_desktop_state's screen_width/height
- serve.rs: rewrite_coord_desc re-injected at both tools/list sites

Install scripts simplified to Rust-only (Swift backend retired upstream).

Fix platform-linux cross-compilation: cfg-gate wayland::PORTAL_LIBEI_ENABLED
reference in health_report.rs for non-linux hosts.

* fix(cua-driver): distinguish desktop-scope from window cache miss in denormalize_args

When screenshot_w == 0 (no cached window size), check whether the call
has a pid/window_id target before falling back to screen-size basis.
Window-scoped clicks with a cache miss now correctly leave coordinates
as-is instead of mapping them against the screen dimensions.

* fix(cua-driver): use screenshot pixels not logical points for desktop-scope coord basis

ingest_screen_size was reading screen_width/height (logical display
points from CGDisplayBounds) for get_desktop_state, but the model
operates on the screenshot PNG whose dimensions are physical pixels.
On Retina displays (2x) this caused every desktop-scope click to land
at half the intended position.

Read screenshot_width/height instead — the same basis normalize_result
rewrites to 1000. Updated test to use realistic Retina values
(screen 1920, screenshot 3840) to catch 1:1 masking.

* fix(cua-driver): split screen cache into logical (move_cursor) and physical (desktop-scope)

The single screen_cache was being written by both get_screen_size
(logical points, e.g. 1920x1080) and get_desktop_state (physical
pixels, e.g. 3840x2160). Since move_cursor operates in CGEvent screen
points (logical) while desktop-scope clicks operate in screenshot
pixels (physical), whichever tool ran last would corrupt the other's
denormalization basis — causing 2x overshoot or 1/2 offset on Retina.

Split into:
- SCREEN_SIZE: logical points, written by get_screen_size, consumed by
  move_cursor (screen_basis=true)
- DESKTOP_SCREENSHOT_SIZE: physical pixels, written by get_desktop_state,
  consumed by desktop-scope click fallback (screenshot_w==0, no pid)

Added cross-contamination isolation tests.

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-07-03 06:27:00 +00:00

119 lines
5.5 KiB
Bash
Executable file

#!/bin/bash
# cua-driver installer — download and install the Rust implementation.
# Downloads the latest release from GitHub Releases and wires it into the user's PATH.
#
# Usage (from README + release body):
# /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/QwenLM/qwen-code/main/packages/cua-driver/scripts/install.sh)"
#
# Flags:
# --bin-dir <path> install the cua-driver wrapper to <path> instead of
# ~/.local/bin (e.g. /usr/local/bin — that target needs sudo)
# --no-modify-path skip auto-appending an `export PATH=...` line to your
# shell rc when ~/.local/bin is missing from PATH
# --backend=rust explicit Rust backend (no-op; Rust is the only option)
# --experimental-rust legacy alias for --backend=rust (no-op)
# --backend=swift retired Swift backend (no-op; accepted for compat)
#
# Env overrides:
# CUA_DRIVER_RS_VERSION=0.7.0 pin a specific Rust release tag
# CUA_DRIVER_VERSION=0.7.0 legacy alias for CUA_DRIVER_RS_VERSION
# CUA_DRIVER_RS_INSTALL_DIR=PATH same as --bin-dir
# CUA_DRIVER_BIN_DIR=PATH legacy alias for --bin-dir
# CUA_DRIVER_NO_MODIFY_PATH=1 same as --no-modify-path
#
# Uninstall:
# /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/QwenLM/qwen-code/main/packages/cua-driver/scripts/uninstall.sh)"
set -euo pipefail
BIN_DIR="${CUA_DRIVER_BIN_DIR:-$HOME/.local/bin}"
NO_MODIFY_PATH="${CUA_DRIVER_NO_MODIFY_PATH:-0}"
# Rust implementation delegation target. The Rust install logic is a private
# helper script colocated with this one — _install-rust.sh — so that
# this directory holds the single user-facing install.sh per platform.
# The Rust path below either execs the on-disk helper (dev /
# checked-out-tree case) or curls this URL and pipes it to bash.
RUST_INSTALLER_URL="https://raw.githubusercontent.com/QwenLM/qwen-code/main/packages/cua-driver/scripts/_install-rust.sh"
# Lightweight flag parsing (avoid getopt; macOS getopt is GNU-incompatible).
#
# Two-pass shape:
# 1. Walk all argv and collect every unrecognised arg into FORWARDED_ARGS.
# That bucket is what we'd hand off to the Rust installer. Recognised
# shared flags (--bin-dir, --no-modify-path) are consumed in this pass
# and applied to local state.
# 2. Always exec the Rust installer with FORWARDED_ARGS. Backend flags
# (--backend=rust/swift, --experimental-rust) are no-ops for compat.
#
# This lets backend flags appear at any position, lets `--` end flag
# parsing without breaking forwarding, and keeps the Rust installer's argv
# shape bit-compatible.
FORWARDED_ARGS=()
PASSTHROUGH=0
while [[ $# -gt 0 ]]; do
if [[ "$PASSTHROUGH" == "1" ]]; then
FORWARDED_ARGS+=("$1"); shift; continue
fi
case "$1" in
--experimental-rust) shift ;; # legacy alias (no-op)
--backend=rust) shift ;; # explicit Rust (no-op)
--backend=swift) shift ;; # retired Swift (no-op)
--backend=*)
printf 'error: unknown backend %q; supported: rust\n' "${1#*=}" >&2
exit 2
;;
--bin-dir)
if [[ -z "${2:-}" || "${2:0:1}" == "-" ]]; then
printf 'error: --bin-dir requires a value\n' >&2
exit 2
fi
BIN_DIR="$2"; FORWARDED_ARGS+=("$1" "$2"); shift 2 ;;
--bin-dir=*) BIN_DIR="${1#*=}"; FORWARDED_ARGS+=("$1"); shift ;;
--no-modify-path) NO_MODIFY_PATH=1; FORWARDED_ARGS+=("$1"); shift ;;
--) PASSTHROUGH=1; shift ;; # forward the rest verbatim
*) FORWARDED_ARGS+=("$1"); shift ;;
esac
done
# --- Delegate to the Rust installer ----------------------------------------
printf 'note: installing qwen-cua-driver via the Rust implementation.\n' >&2
if [[ -n "${CUA_DRIVER_BIN_DIR:-}" && -z "${CUA_DRIVER_RS_INSTALL_DIR:-}" ]]; then
export CUA_DRIVER_RS_INSTALL_DIR="$BIN_DIR"
fi
if [[ -n "${CUA_DRIVER_VERSION:-}" && -z "${CUA_DRIVER_RS_VERSION:-}" ]]; then
export CUA_DRIVER_RS_VERSION="$CUA_DRIVER_VERSION"
fi
if [[ -n "${CUA_DRIVER_NO_MODIFY_PATH:-}" && -z "${CUA_DRIVER_RS_NO_MODIFY_PATH:-}" ]]; then
export CUA_DRIVER_RS_NO_MODIFY_PATH="$CUA_DRIVER_NO_MODIFY_PATH"
fi
# Prefer the on-disk copy when this script is running from a checked-out
# tree (dev / CI). Falls back to curling the canonical URL for the
# `curl ... | bash` install path, where $BASH_SOURCE is unset / -.
LOCAL_RUST_INSTALLER=""
if [[ -n "${BASH_SOURCE[0]:-}" && "${BASH_SOURCE[0]}" != "-" && -f "${BASH_SOURCE[0]}" ]]; then
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
CANDIDATE="$SCRIPT_DIR/_install-rust.sh"
if [[ -f "$CANDIDATE" ]]; then
LOCAL_RUST_INSTALLER="$CANDIDATE"
fi
fi
# macOS ships bash 3.2, which trips `set -u` when expanding an empty
# array via "${arr[@]}" — guard with the +alt-value pattern so the
# zero-arg case becomes a literal no-expansion.
if [[ -n "$LOCAL_RUST_INSTALLER" ]]; then
exec /bin/bash "$LOCAL_RUST_INSTALLER" ${FORWARDED_ARGS[@]+"${FORWARDED_ARGS[@]}"}
else
if ! command -v curl >/dev/null 2>&1; then
printf 'error: curl not found on PATH; cannot fetch %s\n' "$RUST_INSTALLER_URL" >&2
exit 1
fi
# `exec` so the Rust installer replaces this process.
RUST_INSTALLER_SCRIPT="$(curl -fsSL "$RUST_INSTALLER_URL")" || {
printf 'error: failed to download Rust installer from %s\n' "$RUST_INSTALLER_URL" >&2
exit 1
}
exec /bin/bash -c "$RUST_INSTALLER_SCRIPT" cua-driver-rs-install ${FORWARDED_ARGS[@]+"${FORWARDED_ARGS[@]}"}
fi