mirror of
https://github.com/lahfir/agent-desktop.git
synced 2026-08-03 12:46:03 +00:00
Settle the Playwright-grade reliability contract in agent-desktop-core before the Windows/Linux adapters are built, so they inherit it instead of redesigning it. Every command now observes, waits, verifies, and reports honestly instead of firing blindly. Highlights: capability-supertrait split of PlatformAdapter with not_supported() defaults; canonical role/state vocabulary with live `is --property visible`; display enumeration (`list-displays`) and honest `--screen` with scale factor; truthful Automation permission; `native_id` identity spine; window-id-first resolution; serializable `LocatorQuery` with live `find`; default-on auto-wait before every ref action; three-way `hit_test` occlusion gate; `scroll_into_view` in core; core accessible-name precedence; typed `ActionStep` delivery tier; `ProcessState` and `APP_UNRESPONSIVE`; `LaunchOptions`; baseline-diff desktop signals (`wait --event`); typed clipboard (`Text`/`Image`/`FileUrls`); mouse modifier chords and `mouse-wheel`. Hardened through a 35-reviewer pass with independent validation and a green live e2e gate (109/0), plus a head-vs-main performance comparison harness. BREAKING CHANGE: default-on auto-wait changes the timing of every previously-untouched ref-action call (bounded 5000 ms default; `--timeout-ms 0` restores single-shot). `ENVELOPE_VERSION` is now `2.1` (adds the `APP_UNRESPONSIVE` code and process state in error details). FFI ABI major is `3` (append-only struct evolution; `wait --event` is intentionally not exposed over FFI). The legacy string clipboard API is removed in favor of typed content. `key-down`/`key-up` fail closed until daemon-owned held input exists. `close-app` verifies termination and the osascript fallback path is removed. `--text` matching is subtree containment: `find --text X --first` returns the outermost matching container.
134 lines
4.4 KiB
Rust
134 lines
4.4 KiB
Rust
use std::path::PathBuf;
|
|
use std::process::Command;
|
|
|
|
fn main() {
|
|
println!("cargo:rerun-if-changed=src/system/launch_bridge.m");
|
|
println!("cargo:rerun-if-changed=src/system/appkit_bridge.m");
|
|
println!("cargo:rerun-if-changed=src/system/screen_bridge.m");
|
|
println!("cargo:rerun-if-env-changed=TARGET");
|
|
println!("cargo:rerun-if-env-changed=MACOSX_DEPLOYMENT_TARGET");
|
|
if std::env::var("CARGO_CFG_TARGET_OS").as_deref() != Ok("macos") {
|
|
return;
|
|
}
|
|
let target = required_target();
|
|
let deployment = deployment_target();
|
|
let version = std::env::var("CARGO_PKG_VERSION").unwrap_or_else(|_| "unknown".into());
|
|
println!("cargo:rustc-env=AGENT_DESKTOP_MACOS_HELPER_BUILD_ID={version}:{target}:{deployment}");
|
|
let Some(out_dir) = std::env::var_os("OUT_DIR").map(PathBuf::from) else {
|
|
eprintln!("OUT_DIR is required");
|
|
std::process::exit(1);
|
|
};
|
|
let object = out_dir.join("launch_bridge.o");
|
|
let appkit_object = out_dir.join("appkit_bridge.o");
|
|
let screen_object = out_dir.join("screen_bridge.o");
|
|
let archive = out_dir.join("libagent_desktop_launch_bridge.a");
|
|
run(
|
|
Command::new("xcrun")
|
|
.args(["--sdk", "macosx", "clang"])
|
|
.args(["-fobjc-arc", "-fblocks", "-target", &target])
|
|
.arg(format!("-mmacosx-version-min={deployment}"))
|
|
.args([
|
|
"-Wall",
|
|
"-Wextra",
|
|
"-Werror",
|
|
"-c",
|
|
"src/system/appkit_bridge.m",
|
|
"-o",
|
|
])
|
|
.arg(&appkit_object),
|
|
"compile Objective-C AppKit bridge",
|
|
);
|
|
run(
|
|
Command::new("xcrun")
|
|
.args(["--sdk", "macosx", "clang"])
|
|
.args(["-fobjc-arc", "-fblocks", "-target", &target])
|
|
.arg(format!("-mmacosx-version-min={deployment}"))
|
|
.args([
|
|
"-Wall",
|
|
"-Wextra",
|
|
"-Werror",
|
|
"-c",
|
|
"src/system/launch_bridge.m",
|
|
"-o",
|
|
])
|
|
.arg(&object),
|
|
"compile Objective-C launch bridge",
|
|
);
|
|
run(
|
|
Command::new("xcrun")
|
|
.args(["--sdk", "macosx", "clang"])
|
|
.args(["-fobjc-arc", "-target", &target])
|
|
.arg(format!("-mmacosx-version-min={deployment}"))
|
|
.args([
|
|
"-Wall",
|
|
"-Wextra",
|
|
"-Werror",
|
|
"-c",
|
|
"src/system/screen_bridge.m",
|
|
"-o",
|
|
])
|
|
.arg(&screen_object),
|
|
"compile Objective-C screen bridge",
|
|
);
|
|
run(
|
|
Command::new("xcrun")
|
|
.args(["--sdk", "macosx", "ar", "rcs"])
|
|
.arg(&archive)
|
|
.arg(&object)
|
|
.arg(&appkit_object)
|
|
.arg(&screen_object),
|
|
"archive Objective-C launch bridge",
|
|
);
|
|
println!("cargo:rustc-link-search=native={}", out_dir.display());
|
|
println!("cargo:rustc-link-lib=static=agent_desktop_launch_bridge");
|
|
println!("cargo:rustc-link-lib=framework=AppKit");
|
|
}
|
|
|
|
fn required_target() -> String {
|
|
match std::env::var("TARGET") {
|
|
Ok(target)
|
|
if matches!(
|
|
target.as_str(),
|
|
"aarch64-apple-darwin" | "x86_64-apple-darwin"
|
|
) =>
|
|
{
|
|
target
|
|
}
|
|
Ok(target) => {
|
|
eprintln!("unsupported macOS launch bridge target: {target}");
|
|
std::process::exit(1);
|
|
}
|
|
Err(error) => {
|
|
eprintln!("TARGET is required: {error}");
|
|
std::process::exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn deployment_target() -> String {
|
|
let deployment = std::env::var("MACOSX_DEPLOYMENT_TARGET").unwrap_or_else(|_| "10.15".into());
|
|
let supported = deployment
|
|
.split_once('.')
|
|
.and_then(|(major, minor)| Some((major.parse::<u32>().ok()?, minor.parse::<u32>().ok()?)))
|
|
.is_some_and(|version| version >= (10, 15));
|
|
if supported {
|
|
deployment
|
|
} else {
|
|
eprintln!("MACOSX_DEPLOYMENT_TARGET must be 10.15 or newer");
|
|
std::process::exit(1);
|
|
}
|
|
}
|
|
|
|
fn run(command: &mut Command, label: &str) {
|
|
match command.status() {
|
|
Ok(status) if status.success() => {}
|
|
Ok(status) => {
|
|
eprintln!("{label} failed with {status}");
|
|
std::process::exit(1);
|
|
}
|
|
Err(error) => {
|
|
eprintln!("{label}: {error}");
|
|
std::process::exit(1);
|
|
}
|
|
}
|
|
}
|