Metadata-Version: 2.5
Name: uictl
Version: 0.1.2
Summary: Linux desktop UI automation control CLI based on dogtail / AT-SPI
Project-URL: Repository, https://github.com/lin-calvin/uictl
Project-URL: Issues, https://github.com/lin-calvin/uictl/issues
Author-email: calvinweb <calvinchaochao@outlook.com>
License-Expression: GPL-3.0-or-later
License-File: LICENSE
Keywords: accessibility,at-spi,automation,cli,dogtail,gui,testing,wayland,x11
Classifier: Environment :: X11 Applications
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Testing
Classifier: Topic :: Utilities
Requires-Python: >=3.10
Requires-Dist: dasbus>=1.7
Requires-Dist: dogtail>=2.0.0
Description-Content-Type: text/markdown

# uictl

Linux 桌面 UI 自动化控制 CLI，基于 [dogtail](https://gitlab.com/dogtail/dogtail) / AT-SPI
无障碍树。搜索窗口、浏览控件树、点击控件、输入文本、等待/断言，一条命令搞定。

```bash
uictl apps                              # 列出所有应用
uictl windows gnome-text-editor         # 列出应用的窗口
uictl tree gnome-text-editor --depth 4  # 打印窗口控件树
uictl find gnome-text-editor "role=button"
uictl inspect gnome-text-editor "name=保存"
uictl click gnome-text-editor "role=button[name=保存]"
uictl type gnome-text-editor "role=text" --text "hello"
uictl wait  gnome-text-editor "name=保存" --visible
uictl expect gnome-text-editor "name=保存" --visible --sensitive
```

## 安装

```bash
# 轻量命令（不碰无障碍树）：直接跑
uvx uictl --version
uvx uictl install-skill        # 注入 agent 技能到 ~/.agents/skills/uictl/

# 完整 UI 命令：用系统 Python 跑（gi 无 PyPI wheel，只能来自系统包；
# uictl 在 gi 缺失时自动注入系统 site-packages 到 sys.path，无需 PYTHONPATH）
uvx --python /usr/bin/python3 uictl apps

# 或持久安装
uv tool install uictl --python /usr/bin/python3
```

依赖系统包（AT-SPI typelib，无法 pip 安装）：
`gir1.2-atspi-2.0 python3-gi python3-pyatspi`
（Wayland 下如需输入注入：`gnome-ponytail-daemon`，GNOME 46+ 已封禁注入，见下）。

本地开发（本仓库）：

```bash
cd ~/projects/uictl
uv venv .venv --python $(which python3) --system-site-packages
uv pip install -e .
```

## 子命令

| 子命令 | 作用 | Wayland 可用 |
|---|---|---|
| `apps` | 列出应用（`--json`） | ✅ |
| `windows [APP]` | 列出窗口 | ✅ |
| `tree APP` | 控件树（`--depth` `--path` `--json`） | ✅ |
| `find APP SEL` | 搜索控件，输出 path（`--scope` `--json`） | ✅ |
| `inspect APP SEL` | 控件详情（状态/文本/坐标/Action 列表） | ✅ |
| `click APP SEL` | 点击：**AT-SPI Action 接口优先**，无 Action 时回退注入（`--action NAME`） | ✅ Action / ⚠️ 注入 |
| `dblclick APP SEL` | 双击 | ⚠️ 需注入 |
| `hover APP SEL` | 悬停 | ⚠️ 需注入 |
| `type APP SEL --text X` | 输入：默认 fill（EditableText 直接赋值）✅；`--keystrokes` 逐键 ⚠️ 需注入 | ✅ / ⚠️ |
| `key COMBO` | 组合键，如 `"<Control>a"` | ⚠️ 需注入 |
| `wait APP SEL [--visible] [--sensitive] [--focused] [--gone]` | 轮询等待条件成立 | ✅ |
| `expect APP SEL [--text S] [--role R] ...` | 断言，失败 exit 1 | ✅ |
| `install-skill [--dir DIR] [--force]` | 注入 agent 技能到 `~/.agents/skills/uictl/` | ✅ |

> ⚠️ 需注入 = 需要原始输入注入（X11 会话原生支持；Wayland 需 RemoteDesktop
> 后端）。在只读环境（GNOME 46+ Wayland）下这些命令会**报清晰错误**而不是
> 抛 traceback。

## 选择器语法（Playwright 风格）

```
role=button[name=保存]         # 引擎 + [属性...]
name=保存                      # 单属性
text=请输入                    # 匹配 node.text 子串
path=0:2:3                     # 从应用根开始的索引路径（find 输出的 path 可直接用）
保存                           # 裸字符串 = name
```

支持属性：`name` `role`(role_name) `desc`(description) `label` `id`(accessible_id) `text`。
值含空格需加引号：`name="保存 文档"`。

**strict 模式**：`inspect`/`click` 等要求唯一匹配，命中多个会报错并列出全部匹配，
用 `--index N` 指定；`--scope` 限定在父节点内搜索；`--win` 指定多窗口应用的窗口。

## 平台限制说明（为什么有的命令报错）

- **读**（apps/windows/tree/find/inspect/wait/expect）走 AT-SPI 总线，Wayland 全可用。
- **控**（click/type-fill）走 **AT-SPI 接口**（Action/EditableText/Component），
  应用自己实现，Wayland 全可用——这是 GNOME 给无障碍"控制"设计的官方通道。
- **原始输入注入**（key/hover/dblclick/type-keystrokes）在 X11 走 XTEST；
  在 Wayland 被设计成受控特权（需 RemoteDesktop portal 授权）。
  另外 GNOME 46+ 移除了 `org.gnome.Shell.Introspect.GetWindows`，
  gnome-ponytail-daemon 依赖它做坐标换算，因此 dogtail 的注入在
  GNOME 46+ Wayland 上整体不可用 → uictl 会报清晰错误。

## 退出码

- `0` 成功
- `1` 未找到 / 断言失败 / strict 多匹配 / 平台不支持
- `130` 用户中断

## Roadmap

- `screenshot`（GNOME 50 需走 XDG Desktop Portal，`core.take_screenshot` 已预留）
- RemoteDesktop portal 输入后端（Wayland 注入的官方通道，需用户授权）

## 发布到 PyPI

```bash
cd ~/projects/uictl
uv build                                    # 构建 sdist + wheel（产出 dist/）
uv publish                                 # 需要 PyPI API token 凭据
```

凭据（三选一）：

- `~/.pypirc`（`[pypi] username=__token__ password=pypi-xxx`）——本机已配置
- 环境变量：`UV_PUBLISH_TOKEN=pypi-xxx uv publish`
- 交互输入：`uv publish --username __token__`（会提示输入密码）

> ⚠️ 版本一旦发布不可覆盖，改代码后记得 bump `pyproject.toml` 的 `version`。
> 正式发布前可先试水 Test PyPI：
> `uv publish --publish-url https://test.pypi.org/legacy/ --check-url https://test.pypi.org/simple/`
> （需要 test.pypi.org 的 token，写到 `~/.pypirc` 的 `[testpypi]` 段）。
