Skill839 repo starsupdated 2d ago
agent-desktop-ffi
The agent-desktop-ffi library provides C-ABI bindings for desktop automation operations across macOS, Windows, and Linux, enabling direct C function calls to interact with platform accessibility APIs. Use this when integrating desktop automation capabilities into non-Rust applications via languages like Python or C that can load shared libraries, ensuring strict adherence to main-thread-only execution on macOS, proper handle lifecycle management, and the release-ffi build profile to prevent runtime panics.
Install in Claude Code
Copygit clone --depth 1 https://github.com/lahfir/agent-desktop /tmp/agent-desktop-ffi && cp -r /tmp/agent-desktop-ffi/skills/agent-desktop-ffi ~/.claude/skills/agent-desktop-ffiThen start a new Claude Code session; the skill loads automatically.
Definition
SKILL.md
# agent-desktop-ffi Direct C-ABI access to every PlatformAdapter operation. Build the cdylib with the workspace's `release-ffi` profile: ```sh cargo build --profile release-ffi -p agent-desktop-ffi ``` The output is `target/release-ffi/libagent_desktop_ffi.dylib` (`.so` on Linux, `.dll` on Windows) plus a committed C header at `crates/ffi/include/agent_desktop.h`. Four reference topics, loaded as needed: - [ownership.md](references/ownership.md) — who allocates / who frees, for every `*mut T` the FFI hands back to the caller. - [error-handling.md](references/error-handling.md) — errno-style last-error contract, enum validation, panic boundary. - [threading.md](references/threading.md) — macOS main-thread rule, AXIsProcessTrusted inheritance when Python/Node dlopens the cdylib, and the single-owner handle invariant. - [build-and-link.md](references/build-and-link.md) — minimum working example for Python ctypes and a C program that links the dylib. ## ⚠ Core constraints before you integrate - **Main thread only (macOS).** Call every adapter-touching entrypoint (`ad_get_tree`, `ad_resolve_element`, `ad_execute_action`, `ad_screenshot`, clipboard, launch/close, window ops, observation, notifications, etc.) from the process's main thread. The FFI enforces this at runtime in **every build profile** — a worker-thread call returns `AD_RESULT_ERR_INTERNAL` with a diagnostic last-error. On non-macOS platforms the check is a compile-time true; there is no runtime cost. - **Release profile.** `cargo build --release` produces `panic = "abort"` — any Rust panic inside an `extern "C"` fn will `SIGABRT` the host. Use `--profile release-ffi` to get the correct `panic = "unwind"` profile. CI enforces this. - **Last-error lifetime.** Pointers returned by `ad_last_error_*` remain valid across any number of subsequent *successful* FFI calls on the same thread. Only the next failing call rotates them. Cache the pointer once, read it as many times as you need. - **Handle release.** Every `ad_resolve_element` result must be released with `ad_free_handle(adapter, handle)` on the same adapter that produced it. On macOS this balances the internal `CFRetain`; on Windows/Linux the call is a no-op but safe to issue. - **Action policy.** `ad_execute_action` uses the headless policy by default, matching CLI ref commands: no focus stealing and no cursor movement. Use `ad_execute_action_with_policy(..., AD_POLICY_KIND_FOCUS_FALLBACK, ...)` only when focus-changing behavior is intended, and `AD_POLICY_KIND_PHYSICAL` only for explicit physical/headed input semantics. - **Text input privacy.** On macOS, explicit focus/physical policy can use the clipboard briefly for non-ASCII text insertion. Keep the default headless policy or set values directly for sensitive text when the target supports it. - **Enum discriminants.** Every `#[repr(i32)]` enum field is validated at the C boundary — invalid discriminants return `AD_RESULT_ERR_INVALID_ARGS` instead of undefined behavior. - **ABI is unstable before 1.0.** The header lists the exact current shapes. Anything added or reordered in a later patch is a breaking change; pin the version of libagent_desktop_ffi you link against. - **`ad_get_tree` returns a raw adapter tree, not the CLI snapshot.** Ref IDs are always null, no skeleton/drill-down pipeline is wired through, and `interactive_only` / `compact` follow adapter semantics which may diverge slightly from the CLI's post-processed shape. Use `ad_find` + `ad_get` / `ad_is` for point lookups, or invoke the CLI if you need CLI-parity JSON snapshots.
More from this repository