Skill1.1k estrellas del repoactualizado 3d 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.
Instalar en Claude Code
Copiargit 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-ffiDespués abre una sesión nueva de Claude Code; el skill carga automáticamente.
Definición
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`.
A Python ctypes smoke harness lives at `tests/ffi-python/smoke.py` and
serves as a worked end-to-end example covering the ABI handshake, struct
size validation, `ad_version`, and the snapshot pipeline leg. See
`tests/ffi-python/README.md` for usage.
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) — host-thread contract,
cross-process mutation serialization, AXIsProcessTrusted inheritance,
and adapter-bound native handles.
- [build-and-link.md](references/build-and-link.md) — ABI handshake,
struct size validation, minimal C and Python examples, observe-act
workflow, and prebuilt archive locations.
## Observe-act workflow (canonical path)
```
ad_init(AD_ABI_VERSION_MAJOR) // verify header ↔ dylib match
adapter = ad_adapter_create_with_session("s1") // or ad_adapter_create()
rc = ad_snapshot(adapter, "Finder", 0, 10, false, false, &json_out)
// parse json_out: locate snapshot-qualified refs in data.tree
ad_free_string(json_out)
// build action:
AdAction act = {0}; act.kind = AD_ACTION_KIND_CLICK;
rc = ad_execute_by_ref(adapter, "@s8f3k2p9:e5", NULL, &act, 0, &result_out)
ad_free_string(result_out)
ad_adapter_destroy(adapter)
```
`ad_snapshot` returns a `{version, ok, command, data}` JSON envelope
identical to the CLI output. The `data.tree` field contains snapshot-qualified
ref IDs for interactive elements. Pass a qualified ref, or a legacy bare ref
plus its explicit `snapshot_id`, to `ad_execute_by_ref` to drive the pipeline
(RefStore load → strict resolution → actionability preflight → dispatch).
## Core constraints
- **ABI handshake.** Call `ad_init(AD_ABI_VERSION_MAJOR)` once after loading the
dylib. A mismatch between the compiled-in constant and the loaded dylib returns
`AD_RESULT_ERR_INVALID_ARGS` — abort rather than proceed. You can also read the
raw dylib major via `ad_abi_version()` for diagnostic display. New `ad_*` symbols
and new error codes are additive (no bump required); removed or layout-changed
symbols increment the major.
- **Session adapters.** `ad_adapter_create_with_session("session-id")` associates
the adapter with a session namespace for refmap persistence — the same as CLI
`--session <id>`. A null `snapshot_id` is valid only for a qualified ref;
legacy bare `@eN` refs require an explicit snapshot ID. Session IDs: 1–64
chars, ASCII alphanumeric / `-` / `_`.
Invalid IDs return null (check `ad_last_error_*`).
- **Structured session trace (no ABI change).** File-based JSONL tracing activates
only when the session has a manifest with `trace: on` from `session start`
(CLI) or equivalent on-disk setup. `ad_adapter_create_with_session` alone does
**not** create trace files. When tracing is active, `command_context()`-backed
commands append to one segment per OS process under
`~/.agent-desktop/sessions/<id>/trace/<pid>-<procTs>.jsonl`. A long-lived host
reuses the same segment filename for all calls in that process. For unstructured
diagnostics regardless of session manifest, use `ad_set_log_callback` (below).
- **Threading and mutation leases.** Adapter entrypoints may be called from any
host thread. Native handles remain bound to their creating adapter and thread.
Desktop mutations acquire the same canonical cross-process interaction lease
as the CLI; reads carry finite deadlines without taking the mutation lock. See
[threading.md](references/threading.md) for the Apple documentation basis and
the read/read, read/mutation, and mutation/mutation ordering matrix.
- **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.
- **ad_last_error_details.** A fourth accessor, `ad_last_error_details()`,
returns a borrowed JSON string carrying structured details (e.g. the
actionability check report on `ACTION_FAILED`, candidate summaries on
`AMBIGUOUS_TARGET`). The details may contain element names, values, and window
titles from the user's screen — treat as sensitive diagnostics and avoid routing
to shared log surfaces.
- **Handle release.** Every `ad_resolve_element_exact` / `ad_find_exact` result must be
released with `ad_free_handle(adapter, &handle)` on the same adapter that
produced it, before that adapter is destroyed. On macOS this balances the
internal `CFRetain`; on Windows/Linux the call is a no-op but safe to issue.
`ad_free_handle` zeroes `handle.ptr` so a follow-up call is a safe no-op.
- **Primary ref-action path.** `ad_execute_by_ref` is the recommended entrypoint
for the observe-act loop: it loads the RefStore, looks up the ref in the refmap
(STALE_REF on miss), runs strict element re-identification (STALE_REF / AMBIGUOUS_TARGET),
runs the live actionability preflight, then dispatches. TypeText and PressKey
default to `focus_fallback` policy (matching CLI `type`/`press-key`); all other
actions default to `headless`. Pass `AD_POLICY_KIND_HEADED` (2) to opt in to
cursor-based faDel mismo repositorio