Metadata-Version: 2.4
Name: harmonyrun
Version: 0.4.10
Summary: A framework for automating HarmonyOS / OpenHarmony devices through LLM agents
Project-URL: Homepage, https://github.com/HarmonyOS-AI/HarmonyRun
Project-URL: Bug Tracker, https://github.com/HarmonyOS-AI/HarmonyRun/issues
Project-URL: Repository, https://github.com/HarmonyOS-AI/HarmonyRun
Author-email: Legend <408486727@qq.com>
License: MIT
License-File: LICENSE
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Information Technology
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Software Development :: Testing
Classifier: Topic :: Software Development :: Testing :: Acceptance
Classifier: Topic :: System :: Emulators
Classifier: Topic :: Utilities
Requires-Python: >=3.11
Requires-Dist: aiofiles>=25.1.0
Requires-Dist: anthropic>=0.67.0
Requires-Dist: httpx[socks]>=0.27.0
Requires-Dist: jinja2>=3.1
Requires-Dist: jsonschema>=4.20.0
Requires-Dist: langchain-anthropic>=0.3
Requires-Dist: langchain-core>=1.0
Requires-Dist: langchain-openai>=1.0
Requires-Dist: langgraph>=1.0
Requires-Dist: mcp>=1.0
Requires-Dist: pillow>=10.0
Requires-Dist: posthog>=6.7.6
Requires-Dist: pydantic>=2.11.10
Requires-Dist: python-dotenv>=1.2.1
Requires-Dist: rich>=14.1.0
Provides-Extra: all
Requires-Dist: langchain-google-genai>=2.0; extra == 'all'
Requires-Dist: langchain-ollama>=0.2; extra == 'all'
Provides-Extra: dev
Requires-Dist: bandit>=1.8.6; extra == 'dev'
Requires-Dist: black==25.9.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.3; extra == 'dev'
Requires-Dist: ruff>=0.13.0; extra == 'dev'
Requires-Dist: safety>=3.2.11; extra == 'dev'
Provides-Extra: google
Requires-Dist: langchain-google-genai>=2.0; extra == 'google'
Provides-Extra: ollama
Requires-Dist: langchain-ollama>=0.2; extra == 'ollama'
Description-Content-Type: text/markdown

# HarmonyRun 命令行使用说明

HarmonyRun 是一款通过自然语言驱动真机或模拟器完成自动化任务的命令行工具，面向 **HarmonyOS（鸿蒙）** 平台。

```bash
pip install harmonyrun
```

源码、问题反馈与完整文档：[GitHub - HarmonyOS-AI/HarmonyRun](https://github.com/HarmonyOS-AI/HarmonyRun)

下文示例统一使用 `python3 -m harmonyrun` 的写法；若已激活虚拟环境或通过 `pipx` / `uv tool` 将命令加入 `PATH`，可直接使用 `harmonyrun`，两者等价。

---

## 1. 环境要求


| 项目         | 说明                                                                    |
| ---------- | --------------------------------------------------------------------- |
| **操作系统**   | macOS / Linux / Windows 均可                                            |
| **Python** | 3.11 ~ 3.13                                                           |
| **鸿蒙设备**   | 已安装 **`hdc`**（DevEco Studio 自带，需加入系统 `PATH`）；`hdc list targets` 能看到设备 |
| **LLM 访问** | 使用 `run` / `test` 需要可访问的 LLM 服务及对应 API Key（详见 [第 4 节](#4-配置文件与环境变量)）  |


---

## 2. 安装

### 2.1 推荐：从 PyPI 安装

```bash
python3 -m venv .venv
.venv/bin/python3 -m pip install -U pip
.venv/bin/python3 -m pip install harmonyrun
```

验证安装：

```bash
.venv/bin/python3 -m harmonyrun --version
.venv/bin/python3 -m harmonyrun --help
```

默认已包含 OpenAI / Anthropic 两家 LLM 适配器。需要 Gemini 或 Ollama 时按需追加：

```bash
pip install "harmonyrun[google]"   # Gemini
pip install "harmonyrun[ollama]"   # Ollama
pip install "harmonyrun[all]"      # 全部可选 provider
```

### 2.2 离线：从 wheel 包安装

拿到离线交付包（内含 `dist/` 目录与本说明）时，解压到任意目录。以下命令中的版本号、路径请按本机实际情况替换。

```bash
cd /你的解压目录

python3 -m venv .venv
.venv/bin/python3 -m pip install -U pip

# 若 dist/ 内只有一个 wheel，可以用通配符（不要加引号）
.venv/bin/python3 -m pip install dist/harmonyrun-*-py3-none-any.whl
```

### 2.3 可选：pipx / uv tool

把命令直接装进 `PATH`，无需手动激活虚拟环境：

```bash
pipx install harmonyrun
# 或
uv tool install harmonyrun

# 离线包同理，把包名换成 wheel 路径
pipx install /你的解压目录/harmonyrun-<版本>-py3-none-any.whl
```

---

## 3. 快速上手

1. 确保 `hdc list targets` 能看到设备。
2. 首次运行任意命令会自动在用户目录生成 `config.yaml`：
  ```bash
   python3 -m harmonyrun --help
  ```
3. 按 [第 4 节](#4-配置文件与环境变量) 编辑 `config.yaml` 与 `.env`，至少配置一个可用的 API Key。
4. 运行一个任务：
  ```bash
   python3 -m harmonyrun run "打开设置"
  ```

---

## 4. 配置文件与环境变量

HarmonyRun 的运行时偏好分布在三个地方，**职责定位**如下：

| 层 | 用途 | 优先级 | 典型字段 |
|---|---|---|---|
| **CLI 参数**（如 `--steps 20` / `--perception-mode a11y`） | 单次运行的临时覆盖 | 最高 | 见 [§5 `run` 选项表](#5-run执行单个自然语言任务) |
| **`config.yaml`**（`~/.config/harmonyrun/config.yaml`） | 持久化的用户偏好 | 中 | 全部 schema 字段 |
| **环境变量** | 仅限 secrets / 部署级路径 / 运行时调试开关；**不承担**业务字段覆盖 | 视用途分两类 | 见 [§4.3](#43-env-与环境变量) 白名单 |

业务字段（`steps` / `perception_mode` / `model` / `temperature` 等）**只**通过 CLI 或 `config.yaml` 改 —— 不要试图用环境变量绕过。

### 4.1 配置读取优先级（从高到低）

1. 命令行 `-c` / `--config` 指定的 YAML 文件
2. 环境变量 `HARMONYRUN_CONFIG` 指向的 YAML 文件
3. 用户配置目录下的 `config.yaml`
4. 若上述文件不存在，首次运行时会把仓库内置的 `config_example.yaml` 拷贝到该位置

### 4.2 用户配置目录

`config.yaml` 与 `.env` 始终位于 **`~/.config/harmonyrun/`**：

| 系统 | 用户配置目录 |
| ---- | -------------------------------------------------- |
| **macOS / Linux / Windows** | `~/.config/harmonyrun/`（受 `XDG_CONFIG_HOME` 影响） |

历史上 macOS / Windows 走的是各自系统约定（`~/Library/Application Support/harmonyrun/`、`%APPDATA%/harmonyrun/`），统一到 `~/.config/harmonyrun/` 是为了和 `gh` / `starship` / `ripgrep` 等工具保持一致。**老用户首次运行 HarmonyRun 时会自动把旧路径下的 `config.yaml` 拷贝到新位置**（原文件保留不动，确认无误后可手动删除）。


### 4.3 `.env` 与环境变量

HarmonyRun 在 `run` / `test` / `farm run` 入口启动时会自动读取两处 `.env`，两次读取都**不会覆盖**已经存在的环境变量：

1. **当前工作目录及父目录**：以 `python-dotenv` 默认规则向上查找 `.env`。适合把密钥跟工程放在一起。
2. **用户配置目录**（见 [4.2](#42-用户配置目录)）下的 `.env`：作为全局兜底。

#### 4.3.1 LLM API Key

把密钥放到 `.env` 而不是 `config.yaml`，更适合团队共享配置模板而密钥各自保管。

可以把下面这样的文件放在**工程当前目录**（推荐）或**用户配置目录**下：

```bash
# 百炼（Anthropic 协议）
ANTHROPIC_API_KEY=sk-sp-xxxxxxxxxxxxxxxx

# 其它提供商（按需）
# GOOGLE_API_KEY=...
# GEMINI_API_KEY=...
# OPENAI_API_KEY=...
```

同时，从 `.env` 里去掉 `config.yaml` 中各角色下的 `kwargs.api_key`，只保留 `base_url`：

```yaml
llm_profiles:
  fast_agent:
    provider: Anthropic
    model: qwen3-vl-plus
    temperature: 0.2
    base_url: https://coding.dashscope.aliyuncs.com/apps/anthropic
```

#### 4.3.2 API Key 的最终优先级（从高到低）

1. `config.yaml` → `llm_profiles.<角色>.kwargs.api_key`（如果填了就直接传给底层 SDK）
2. 系统 / shell 环境变量（`export ANTHROPIC_API_KEY=...`，进程已有的值永远不被 `.env` 覆盖）
3. **当前工作目录及父目录** 下的 `.env`（先于用户配置目录加载）
4. **用户配置目录下** 的 `.env`（最后一层兜底）

> 直白点说：进程一旦持有了某个 `*_API_KEY`，两个 `.env` 都不会覆盖；用户配置目录的 `.env` 仅在 shell env 和 cwd `.env` 都没设置时才生效。

#### 4.3.3 HarmonyRun 系统级环境变量白名单

| 变量 | 用途 | 何时直读 |
| --- | --- | --- |
| `HARMONYRUN_CONFIG` | 指定一个 `config.yaml` 路径，覆盖默认 | 加载配置之前 |
| `XDG_CONFIG_HOME` | 覆盖 `~/.config` 基址 | 解析用户配置目录 |
| `HARMONYRUN_HDC_PATH` / `HDCUTILS_HDC_PATH` | 指定 hdc 可执行路径 | hdcutils 启动 |
| `HARMONYRUN_TELEMETRY` / `HARMONYRUN_TELEMETRY_ENABLED` | 遥测开关 | telemetry 初始化 |
| `HARMONYRUN_STREAM_SCREENSHOTS` | 调试用：流式发送截图（等价于 `logging.stream_screenshots: true`） | 配置加载时 |
| `HARMONYRUN_GITHUB_REPO` / `HARMONYRUN_FEEDBACK_STORAGE_REPO` | `feedback` 子命令：issue 落点仓 / trajectory zip 落点 drop-repo（见 [§10.2](#102-仓库落点与-drop-repo-模型)） | `feedback` 运行时 |

这些是**唯一**允许通过环境变量影响行为的字段。其他需要持久化的偏好请改 `config.yaml`；需要单次调整的请用 CLI。

#### 4.3.4 设备解锁口令

运行任务时 HarmonyRun 会先检查设备电源状态，若处于 `SLEEP` 会自动唤醒并滑开锁屏；**若设备设置了 PIN / 密码锁，需要通过环境变量提供解锁口令**，否则只能滑开但无法通过密码屏。


| 变量名                                        | 用途                                                 |
| ------------------------------------------ | -------------------------------------------------- |
| `HARMONYRUN_DEVICE_UNLOCK_PASSWORD`          | 全局回退口令，所有设备共用                                      |
| `HARMONYRUN_DEVICE_UNLOCK_PASSWORD_<SERIAL>` | 指定设备专用口令；优先级高于全局回退。`<SERIAL>` 中非字母数字字符替换为下划线，并整体大写 |


示例：设备序列号为 `22M0224104000249`，对应变量名为 `HARMONYRUN_DEVICE_UNLOCK_PASSWORD_22M0224104000249`：

```bash
# 全局回退
HARMONYRUN_DEVICE_UNLOCK_PASSWORD=123456

# 指定设备（带连字符或其它符号的序列号，非字母数字都转成 _）
HARMONYRUN_DEVICE_UNLOCK_PASSWORD_22M0224104000249=123456
```

> 口令会在唤醒后自动输入；如果不配置，运行时会在日志中提示 "no HARMONYRUN_DEVICE_UNLOCK_PASSWORD"，此时请确保设备未设置密码锁。

### 4.4 `config.yaml` 精简模板

首次运行会在 `~/.config/harmonyrun/config.yaml` 生成下面的精简模板，可直接在其上修改。完整 schema 见 `src/harmonyrun/config/schema/`，下表只列**常改字段**：

```yaml
# === Agent ===
agent:
  max_steps: 15                  # 单任务最大步数
  reasoning: false               # true=Manager+Executor 两阶段；false=FastAgent 单轮
  streaming: true                # 流式打印 LLM 响应

  # UI 感知模式 —— 决定 LLM 每步看到什么:
  #   a11y       : 仅 a11y 文本（index-based 点击，token 最省）
  #   screenshot : 仅截图（坐标点击，与 use_normalized_coordinates 互斥）
  #   both       : a11y 文本 + 截图（默认）
  perception_mode: both

  # 内置 system / user prompt 的自然语言:
  #   en : 英文（默认，与历史行为一致）
  #   zh : 中文
  # 切换只影响"叙述语言"——工具名 / XML 标签 / 字段名 / 参数名都仍保持
  # 英文（如 click_at、<device_state>、dismiss_after），以保证 LLM 的
  # 解析协议不变。自定义 prompt（HarmonyAgent(prompts=...)）不受影响。
  prompt_language: en

  app_cards:                     # 详见 ./应用卡片.md
    enabled: true
    mode: local                  # local | server | composite

# === LLM Profiles ===
# 每个 agent 角色用哪个 LLM。CLI 的 --provider/--model/--temperature/--base_url
# 会一次覆盖所有 profile；要分别配置请直接改这里。
# kwargs 里可以放 API key（如果不想用环境变量），如 kwargs: { api_key: sk-... }
llm_profiles:
  manager:
    provider: GoogleGenAI
    model: gemini-3.1-flash-lite-preview
    temperature: 0.2
  executor:
    provider: GoogleGenAI
    model: gemini-3.1-flash-lite-preview
    temperature: 0.1
  fast_agent:
    provider: GoogleGenAI
    model: gemini-3.1-flash-lite-preview
    temperature: 0.2
  app_opener:
    provider: GoogleGenAI
    model: gemini-3.1-flash-lite-preview
    temperature: 0.0
  structured_output:
    provider: GoogleGenAI
    model: gemini-3.1-flash-lite-preview
    temperature: 0.0

# === Device ===
device:
  serial: null                   # null = 自动选第一台已连接的设备
  # 仅 `harmonyrun test`：每个 case 启动应用前，先把设备切到目标姿态。
  # 失败采 best-effort（warn + 继续），不会让用例直接 fail。
  orientation: null              # portrait | landscape | portrait_inverted | landscape_inverted | null=不强制
  fold_display: null             # expanded | folded | null=不强制；非折叠屏配 expanded/folded 会被自动跳过
  harmony_sdk_path: null         # HarmonyOS SDK / 命令行工具根：仅模拟器折叠/旋转用（定位 host Emulator 二进制）；null=自动发现；真机无需配

# === Logging ===
logging:
  debug: false                   # 详细日志（也会激活 per-LLM JSON 日志写到 trajectory log/）
  save_trajectory: none          # none | step | action
  trajectory_path: trajectories  # 轨迹根目录
  screen_recording: false        # 屏幕录制（save_trajectory != none 时才生效）

# === Tools ===
tools:
  # 坐标类工具（click_at / click_area / long_press_at）默认启用，作为逃逸手段。
  # a11y 模式下优先用 index 点击（工具描述已写明），index 点不到的目标用坐标兜底。
  # 如需关掉，在这里按名字列出（默认空）。
  disabled_tools: []

# === Telemetry ===
telemetry:
  enabled: false                 # 也可通过 HARMONYRUN_TELEMETRY_ENABLED=false 关闭
```

#### 4.4.1 不在精简模板里、但仍可写的高级字段

下列字段都有合理默认值，专家场景下可写进 `config.yaml`：

- **`agent`**：`name`、`after_sleep_action`、`use_normalized_coordinates`、`filter_a11y_tree`
- **`agent.app_cards`**：`server_url`、`server_timeout`、`server_max_retries`（仅 `mode=server/composite` 用）
- **`device`**：`simplify_tree`、`annotation_max_depth`、`annotation_min_size`、`auto_wake`、`keep_awake`、`keep_awake_interval`
- **`tools`**：`stealth`、`swipe_fast_default`
- **`llm_profiles.<role>`**：`base_url`、`kwargs`

> 字段定义全部在 `src/harmonyrun/config/schema/*.py` 里，是 Python dataclass。

### 4.5 LLM 模型切换要点

- **推荐使用支持视觉的模型**（百炼上的 `qwen3.6-plus` 或 Claude Sonnet 4.x 等）并把 `agent.perception_mode` 设为 `both`（默认）—— LLM 同时看到 a11y 树和截图，能显著提高对复杂界面的理解与点击准确率。
- 如果用的是**纯文本模型**或希望节省 token，把 `agent.perception_mode` 设为 `a11y`：只发文本树，优先用 `click(index=N)` 类工具点击。坐标工具（`click_at` 等）默认仍启用，作为 index 点不到时（如 WebView 内部图标）的逃逸手段；如确实想关掉，写进 `tools.disabled_tools`。
- 极少数场景（如**全屏 Canvas 应用**）a11y 树价值很低，可以试 `agent.perception_mode: screenshot`，让 LLM 主要用坐标点击（坐标工具默认已启用，无需额外配置）。

---

## 5. `run`：执行单个自然语言任务

```bash
python3 -m harmonyrun run "你的任务描述"
```

常用选项：


| 选项                               | 含义                         |
| -------------------------------- | -------------------------- |
| `-c, --config`                   | 指定配置文件                     |
| `-d, --device`                   | 设备序列号或 IP                  |
| `-p` / `-m`                      | LLM 提供商与模型，**必须同时指定**      |
| `--temperature`                  | 采样温度                       |
| `--steps`                        | 单任务最大步数                    |
| `-u`, `--base_url`               | 自定义 API 地址（OpenRouter / Ollama / OpenAI-Like）|
| `--perception-mode`              | `a11y` / `screenshot` / `both` |
| `--reasoning` / `--no-reasoning` | 切换规划推理模式                   |
| `--stream` / `--no-stream`       | 流式输出                       |
| `--save-trajectory`              | `none` / `step` / `action` |
| `--save-uitest-raw` / `--no-save-uitest-raw` | 在 trajectory 目录额外落 `uitest_raw/{idx:04d}.{json,png}`（原始 uitest 树 + 无标注截图）。默认关闭。 |
| `--debug`                        | 开启调试日志                     |


退出码：成功 `0`，失败 `1`。

### 5.1 `run` 的输出件

当 `save_trajectory` 非 `none` 时，在 `logging.trajectory_path`（默认 `trajectories/`，相对当前工作目录）下生成：

```
trajectories/
└── <YYYYMMDD_HHMMSS>_<uuid8>/
    ├── trace.jsonl              # 完整轨迹事件流（含设备 I/O，按行 JSON）
    ├── meta.json                # 本次运行摘要：目标、耗时、token 统计、环境信息
    ├── device_state/            # 每步模型看到的设备状态文本（0000.txt …）
    ├── screenshots/
    │   ├── 0000.jpeg            # 截图帧（每步一张）
    │   └── recording.mp4        # 屏幕录像（screen_recording=true 时）
    ├── log/                     # 每次 LLM 调用的 JSON 日志（logging.debug=true 时）
    └── report.html              # 自包含 HTML 查看器；浏览器直接打开即可回放
```

> 推荐用 `harmonyrun view <轨迹目录>` 打开报告；也可直接在文件管理器双击 `report.html`（本地 `file://` 即可，无需起 server）。

---

## 6. `test`：批量执行测试套件

```bash
python3 -m harmonyrun test /路径/套件.json
python3 -m harmonyrun test --help
```

- 套件为 JSON 格式，需符合 HarmonyRun 测试套件约定（schema 见 `src/harmonyrun/batch/schemas/test_suite.schema.json`）。
- `--case <ID>`：只执行指定用例。
- `--level L0|L1|L2`：按优先级筛选（`L0` 仅最高优先级；`L1` = `L0+L1`；`L2` = 全部）。
- `-d <设备>` 可重复：多台设备组成池，用例自动分配到空闲设备。
- `--save-trajectory none|step|action`：覆盖 `config.yaml` 的 `logging.save_trajectory`；不传时默认 `step`（即便 `config.yaml` 写的是 `none`，`test` 也会自动开到 `step`，否则没法出报告）。
- `-c, --config <路径>`：指定配置文件。

### Suite 级覆盖默认显示姿态

要让某个套件里**所有** case 都在固定的横竖屏 / 内外屏下启动，在 suite JSON 顶层加一个 `defaults.config_overrides` 即可。每个 case setup 阶段会在 `stop_app` 之后、`start_app` 之前自动切到目标姿态，失败采 best-effort（warn + 继续）：

```json
{
  "suite": { "id": "video_landscape", "name": "Video landscape suite", "app_package": "com.example.video" },
  "defaults": {
    "config_overrides": {
      "orientation": "landscape",
      "fold_display": "expanded"
    }
  },
  "test_cases": [ /* ... */ ]
}
```

- `orientation`：`portrait` / `landscape` / `portrait_inverted` / `landscape_inverted`
- `fold_display`：`expanded`（内屏 / 展开） / `folded`（外屏 / 折叠）；非折叠屏设备会自动跳过并 warn
- 不写 = 不强制（保持设备当前姿态）；同名字段在 `config.yaml` 的 `device` 节也可写作全局兜底

### 6.1 `test` 的输出件

每次运行会在 `logging.trajectory_path` 下生成一个**两层**的套件目录：先按 `suite_id` 分目录，再每次跑出一个带时间戳的子目录：

```
trajectories/
└── <suite_id>/
    └── test_YYYYMMDD_HHMMSS_<uuid8>/
        ├── meta.json                 # 套件 meta（schema_version / status / 设备 / 用例计划），运行中持续刷新
        ├── report.json               # 机器可读总报告（符合 report.schema.json）
        ├── report.html               # 自包含 HTML 查看器（双击即可看，无需起 server）
        └── cases/
            └── <case_id>/            # 注意：case 子目录**不带时间戳**（与单跑 run 不同）
                ├── trace.jsonl
                ├── meta.json
                ├── device_state/
                ├── log/              # 每次 LLM 调用的 JSON 日志（logging.debug=true 时）
                ├── screenshots/
                │   ├── 0000.jpeg
                │   └── recording.mp4 # screen_recording=true 时
                └── report.html       # 单个 case 的 HTML 查看器
```

> 旧版本曾产出 `report.md`，已下线 —— HTML 是唯一可视化入口。HTML 在 case 跑完后即时刷新，可以一边跑一边看进度。
> 推荐用 `harmonyrun view <套件目录>` 打开报告（详见 §11）。

---

## 7. `doctor`：环境与设备健康检查

```bash
python3 -m harmonyrun doctor              # 检查当前环境 + 自动选第一台设备
python3 -m harmonyrun doctor -d <serial>  # 指定设备
python3 -m harmonyrun doctor --debug      # 打印每项检查的 detail 行
```

参数：

| 选项 | 含义 |
|---|---|
| `-d, --device <serial>` | 指定要诊断的设备；省略时使用 `config.yaml` 里的 `device.serial`，再不行就 hdc 第一台 |
| `--debug / --no-debug` | 每项检查额外打印一行 `detail`（hdc 路径、版本、SDK env 命中、socket 端口等） |

### 7.1 检查项一览

`doctor` 按顺序跑下面这些检查，前一项致命失败时跳过后续步骤，并在最后汇总 fail / warn 数量。

| 项 | 检查内容 | 失败影响 |
|---|---|---|
| **SDK Version** | 当前 `harmonyrun` 版本 vs GitHub release latest | warn 提示升级 |
| **Config** | 读取 `~/.config/harmonyrun/config.yaml`，缺则按默认模板创建 | fail 阻断后续 |
| **Platform** | 报告 `device.platform`（HarmonyOS 是唯一允许值）及 serial（auto / 显式） | fail 阻断 |
| **Harmony Env** | 探测 `HARMONYRUN_HDC_PATH` / `HDCUTILS_HDC_PATH` / `OHOS_SDK_HOME` / `HARMONYOS_SDK_HOME` / `DEVECO_SDK_HOME` / `HARMONY_HOME` 与 `~/Library/Huawei/Sdk` / `~/Library/OpenHarmony/Sdk` 等常见路径；任一命中则 pass | warn 提示 SDK env 缺失 |
| **HDC** | `hdc` 可执行 + 能 `list targets` | fail 时跳过所有设备相关检查 |
| **Device** | 通过 `hdc` 连上目标设备并拿到 `state=device` | fail 时跳过其后 |
| **Shell** | 设备 shell 通：`date` 命令能回数据 | fail 表示 hdc 转发坏掉 |
| **Device Env** | 设备 `const.product.os.dist.version` / `apiversion` / `software.version` / `hardwareversion` / `getenforce` / 时间 | 任一缺值 warn |
| **uitest** | 设备上的 `uitest` 版本号能读出来 | fail 阻断后续 UI 类检查 |
| **uitest agent** | 比对设备已装的 `agent.so` 与本机 SDK 期望版本（按 uitest version + arch 选）：缺则 warn（socket 时会自动装），版本低于期望也 warn | warn |
| **Socket** | 端到端跑通 socket 链路：agent.so → daemon → `hdc fport` → TCP → Hypium RPC，并打印 `tcp:<local>->{remote_spec}` 与当前 rotation。失败采 warn（运行时第一次 `dump_layout` 失败会粘性熔断 socket 并回退到 shell uitest dumpLayout） | warn 时附上 `scripts/diagnose_uitest_socket.py` 的诊断命令 |
| **Screenshot** | 通过当前 backend 截一张 PNG，输出文件大小 | fail 表示截图链路异常 |
| **UI Tree** | `uitest dumpLayout` 拿到 JSON，统计节点数 | fail 表示布局抓取异常 |

### 7.2 输出示例

```
HarmonyRun Doctor (HarmonyOS)

  SDK Version            0.5.3 (up to date)                         ✓
  Config                 ~/.config/harmonyrun/config.yaml           ✓
  Platform               harmony                                     ✓
  Harmony Env            SDK env detected                            ✓
  HDC                    found, 1 device(s)                          ✓
  Device                 22M0224104000249 (state=device)             ✓
  Shell                  Sat May 17 10:23:11 CST 2026                ✓
  Device Env             os=5.1.0, api=18, selinux=Enforcing         ✓
  uitest                 v5.0.7.200                                  ✓
  uitest agent           v5.0.7.200                                  ✓
  Socket                 tcp:48721 -> localabstract:uitest_socket, rotation=0  ✓
  Screenshot             ok (482 KB)                                 ✓
  UI Tree                312 nodes                                   ✓

  All checks passed.
```

任何 fail / warn 会在结尾按状态分组汇总，并附上 `detail` 字段（如 `pip install --upgrade harmonyrun` / `scripts/diagnose_uitest_socket.py` 等可直接复制粘贴的下一步）。

---

## 8. `mcp`：把原子能力开放给外部 Agent

`mcp` 子命令以 [Model Context Protocol](https://modelcontextprotocol.io) 暴露 HarmonyRun 的**原子能力**（设备发现、UI 感知、点击/滑动等），任意 MCP-aware 客户端（Claude Code / Cursor / Cline / 自研业务 Agent）都能驱动一台 HarmonyOS 真机。

**核心承诺：atomic 模式**——MCP server 自身**不加载任何 LLM**、不消费 `llm_profiles`、缺 `OPENAI_API_KEY` 也能起。每一步动作由**外层 Agent** 决策；HarmonyRun 只负责把原子能力封装好。"一句话甩任务"的黑盒用法继续走 `harmonyrun run` CLI。

### 8.1 启动 server

```bash
python3 -m harmonyrun mcp serve                          # stdio（IDE 集成默认）
python3 -m harmonyrun mcp serve --transport sse          # SSE（本地 HTTP）
python3 -m harmonyrun mcp serve --transport streamable-http
python3 -m harmonyrun mcp serve -c /path/to/config.yaml  # 指定配置（可选）
python3 -m harmonyrun mcp serve --debug                  # 详细日志（stdio 模式日志走 stderr）
```

参数：

| 选项 | 含义 |
|---|---|
| `--transport <stdio\|sse\|streamable-http>` | 传输层；默认 `stdio`，本地 IDE 集成用它即可 |
| `-c, --config <路径>` | 指定 `config.yaml`；缺省时用 `~/.config/harmonyrun/config.yaml`，**配置不存在也能正常起**（atomic 模式无 LLM 依赖） |
| `--debug / --no-debug` | 详细日志 |

### 8.2 IDE 集成示例（Claude Code / Cursor / Cline）

```jsonc
// ~/.claude.json 或对应 IDE 的 MCP 配置
{
  "mcpServers": {
    "harmony": {
      "command": "harmonyrun",
      "args": ["mcp", "serve"]
    }
  }
}
```

加完之后，IDE 里的编码 Agent 就能直接调用 `list_devices` / `connect_device` / `get_ui_state` / `tap_element` 等工具。

### 8.3 暴露的 Tools

每个 session 内**只能持有一台**活跃设备；切设备就先 disconnect 再 connect。

**连接（connection）**

| Tool | 作用 |
|---|---|
| `list_devices()` | 列出 `hdc` 看得到的所有设备（serial + state） |
| `connect_device(serial?)` | 连接到指定设备；省略 `serial` 时自动选第一台在线设备。会替换本 session 已有的活跃设备 |
| `disconnect_device()` | 释放活跃设备的所有资源（uitest daemon / hilog monitor / fport 转发） |

**感知（perception）**

| Tool | 作用 |
|---|---|
| `get_ui_state()` | 一次取回带索引的元素列表、a11y 文本、前台 app 元信息（bundle / ability / page）、屏幕尺寸、`page_signature`（页面指纹，可用于判跳转） |
| `get_screenshot(annotated=true)` | PNG 截图；`annotated=true`（默认值跟 `mcp.screenshot_default_annotated` 一致）会在交互元素上画 SoM 索引标签，配合 `tap_element(index)` 用 |
| `get_recent_events()` | 拉取上次调用以来产生的 toast / dialog / popup 出现-消失事件，用来判断动作的副作用。环形缓冲上限由 `mcp.event_buffer_size` 控制 |

**动作（actions）**

| Tool | 作用 |
|---|---|
| `tap(x, y)` | 设备坐标点击 |
| `tap_element(index)` | 用 `get_ui_state()` 给出的索引点击（推荐，跨分辨率稳） |
| `swipe(x1, y1, x2, y2, duration_ms=1000, fast=false)` | 滑动；`fast=true` 走 fling 路径（滚动惯性） |
| `long_press(x, y)` | 长按 |
| `input_text(text, element_index?, clear=false)` | 文本输入；可选先 tap 一个 element 聚焦，可选清空旧内容 |
| `press_key(keycode)` | 发原始 keycode（对应 `@ohos.multimodalInput.keyCode`） |
| `back()` / `home()` | 系统返回 / Home |
| `system_button(button)` | `back / home / menu / power / volume_up / volume_down` 命名按键 |
| `set_rotation(orientation)` | `portrait / landscape / portrait_inverted / landscape_inverted` |
| `set_fold_display(expanded)` | 折叠屏切换；非折叠设备调用会 `ok=false` 并返回原因，不会假装成功 |
| `open_app(bundle)` | 按 bundleName 启动 app；**仅当 `mcp.expose_open_app=true`（默认）时注册** |

### 8.4 暴露的 Resources

`get_ui_state` / `get_screenshot` / `get_recent_events` 同时以 MCP resource 形式暴露，方便客户端用 resource 订阅而不是每次主动调 tool：

| URI | MIME | 内容 |
|---|---|---|
| `harmony://device/state` | `application/json` | 当前 UIState 的 JSON 表示（与 `get_ui_state` 同结构） |
| `harmony://device/screenshot` | `image/jpeg` | 原始 JPEG 截图（无标注） |
| `harmony://device/screenshot-annotated` | `image/jpeg` | 带元素索引标注的 JPEG 截图 |
| `harmony://device/events` | `application/json` | 最近 drain 的事件 JSON 数组 |

### 8.5 配置项（`config.yaml` → `mcp.*`）

| 字段 | 默认 | 含义 |
|---|---|---|
| `mcp.event_buffer_size` | `50` | `get_recent_events` 一次最多返回多少条事件 |
| `mcp.screenshot_default_annotated` | `true` | `get_screenshot` 默认是否带标注 |
| `mcp.expose_open_app` | `true` | 是否注册 `open_app` 工具（关掉可避免给外层 Agent 直接拉应用的能力） |

### 8.6 `mcp doctor`：上线前体检

把 server 暴露给 IDE 之前先跑一次：

```bash
python3 -m harmonyrun mcp doctor
```

按顺序检查：

1. `hdc` 是否在 PATH / `HARMONYRUN_HDC_PATH`
2. 至少一台 HarmonyOS 设备在线
3. MCP server 能在 atomic 模式下构建出来（捕获 LLM 配置陷阱、依赖缺失等）

任一不过返回退出码 1；通过会提示 `harmonyrun mcp serve` 可以起了。

---

## 9. `farm`：远程群控（让无真机的 CI 也能跑）

服务端编译流水线（Linux CI、Docker、远程 K8s）通常无法连接真机。`farm` 子命令组让一台**有真机的机器**（本地电脑或机房）启动一个 HTTPS 服务端，把连着的真机当成"远程驱动池"——任意可以访问该 endpoint 的客户端都能用 `harmonyrun farm run` 像本地一样跑 LLM agent。

v1 设计取舍：1 人 1 farm（单 token）；不带视频流（agent 只要截图 + a11y）；网络可达性用户自备（Tailscale / FRP / Cloudflare Tunnel）；保持独立模块，不耦合现有 `run` / `test` 命令。

### 9.1 host 端（接真机的那台）

```bash
# 启动 farm server，监听局域网 8080
harmonyrun farm serve --bind 0.0.0.0:8080 --token <YOUR_TOKEN>

# 限定只把指定设备纳入设备池
harmonyrun farm serve --bind 0.0.0.0:8080 --token <T> -d SERIAL1 -d SERIAL2

# 调整 lease TTL（默认 1800s）
harmonyrun farm serve --token <T> --lease-ttl 600
```

参数：

| 参数 | 含义 |
|---|---|
| `--bind HOST:PORT` | 监听地址，默认 `127.0.0.1:8080`；要给远端访问就改 `0.0.0.0:8080` |
| `--token T` | Bearer token，客户端必须带 `Authorization: Bearer T` |
| `--max-devices N` | 限制设备池最多收 N 台（按 `hdc list` 顺序截取） |
| `-d / --device SERIAL` | 显式指定要纳入设备池的设备 serial（可重复） |
| `--lease-ttl SEC` | 默认租约 TTL（秒），客户端心跳掉线后会被回收 |
| `--archive-raw` | 服务端逐 session 归档 **raw uitest 树 + 无标注截图** 到 `<archive-dir>/<sid>/uitest_raw/`（离线复现用）。留在 server 侧，**不回传 client**；client 的 `--save-uitest-raw` 在 farm 模式下被强制关闭并改由本开关承担 |
| `--record-screen` | 服务端逐 session 录 **MP4** 到 `<archive-dir>/<sid>/recording.mp4`（设备→server 本地 pull，不过网） |
| `--archive-dir DIR` | 上述归档根目录，默认 `farm_trajectories` |
| `--debug` | 开 uvicorn debug 日志 |

启动后:
- `GET  /v1/health` 不鉴权,返回设备池容量。可用作 readiness probe
- 其他 v1 路由都要带 token,详见 [src/harmonyrun/farm/server/app.py](https://github.com/HarmonyOS-AI/HarmonyRun/blob/main/src/harmonyrun/farm/server/app.py)

> **trace 分侧**：farm 下 agent trajectory（trace.jsonl / 截图 / device_state / LLM 日志）落在 **client** 本地 `trajectories/<id>/`；重原始产物（raw uitest / 录屏 MP4）由 `--archive-raw` / `--record-screen` 落在 **server** 侧 `farm_trajectories/<sid>/`，**不过网**。设备侧 hilog 事件（toast/dialog）由 server 自动起 monitor、**搭车感知响应**回传 client。

### 9.2 client 端（CI / 无真机机器）

```bash
# 单次 NL 命令
harmonyrun farm run "打开设置并点击 wifi" \
  --remote https://farm.lan:8080 --token <YOUR_TOKEN>

# 指定要租的设备
harmonyrun farm run "..." --remote ... --token ... --device SERIAL1

# 通过环境变量配置（写到 .env 也行）
export HARMONYRUN_FARM_URL=https://farm.lan:8080
export HARMONYRUN_FARM_TOKEN=<T>
harmonyrun farm run "打开设置"
```

`farm run` 与本地 `harmonyrun run` 行为对齐：跑完整 HarmonyAgent loop，agent trajectory 写在 CI 本地 `trajectories/<id>/`，区别仅在于底层 driver 是 `RemoteDriver`（每次原子动作走一次 HTTPS）。感知每步只走 **1 次** `GET /state` 往返（树+截图+hilog 事件一次拿全），不是分开的 screenshot+ui_tree 两次。

辅助子命令：

```bash
harmonyrun farm devices --remote URL --token T                    # 列远端设备池
harmonyrun farm doctor  --remote URL --token T [--device SERIAL]  # 端到端 ping（lease + screenshot + ui_tree 延迟，可指定 serial）
```

### 9.3 网络方案建议

HarmonyRun 故意不内置反向隧道。建议：

- **同一办公网**：farm 直接 `--bind 0.0.0.0:8080`，CI 用内网 IP
- **跨网络**：Tailscale / Cloudflare Tunnel / FRP 把 farm 的 HTTPS 暴露为可达 endpoint
- **生产环境**：在 farm 前挡一层 nginx/Caddy，由它做 TLS 证书 + IP 白名单

token 单值,日志里 `Bearer ...` 自动 mask 成 `***`。不要把 token 提交到 git。

### 9.4 不在 v1 范围

- 多租户 / 配额 / 计费
- 视频流 / WebRTC / 浏览器接管（LLM agent 不需要）
- 内置反向隧道（用户自备）
- `harmonyrun farm test <suite>`（套件运行需要复用 batch 流水线，留作 v1.1）
- hilog 事件流实时透传（v1 客户端 `drain_device_events()` 返回 `[]`，v1.1 走 WebSocket）

### 9.5 环境变量

| 变量 | 作用 |
|---|---|
| `HARMONYRUN_FARM_URL` | 远端 farm endpoint，CLI `--remote` 的兜底 |
| `HARMONYRUN_FARM_TOKEN` | Bearer token，CLI `--token` 的兜底 |
| `HARMONYRUN_FARM_DEVICE` | 默认 lease 的设备 serial（可选） |

---

## 10. `feedback`：一键提交测试反馈到 GitHub issue

测试人员跑用例发现问题后，`harmonyrun feedback` 一条命令完成：

- 找到本次（或指定的）trajectory 目录
- 读 `meta.json` 预填 issue 正文（任务目标、设备、模型、设备操作统计）
- 自动把 `console.log` 末尾 200 行贴进正文（测试通常先在终端看到异常）
- 把 trajectory 打成 zip 上传到**专用 drop-repo** 的 release（默认 `HarmonyOS-AI/HarmonyRun-Feedback`，剔除录屏 mp4；见 [§10.2](#102-仓库落点与-drop-repo-模型)）
- 通过 `gh` CLI 创建 issue 并打 label `test-feedback` `bug` `severity:<level>`
  （这些 label 只是元数据；当前账号若无权限打 label，会自动降级为无 label
  重试，保证 issue 仍能建出来）。`.github/workflows/claude-on-feedback.yml`
  对**任何新建 issue** 都触发 Claude Code 自动分析——**不再依赖任何 label**
  （私有仓，能开 issue 的都是内部人员，刻意降低门槛）
- 自动打开浏览器跳到 issue 页让测试人员补充

```bash
# 自动用 trajectories/ 下最新一个
harmonyrun feedback

# 指定 trajectory 目录
harmonyrun feedback ./trajectories/20260526_142319_a8f4b2c1

# 不带 trajectory 附件，只创建 issue
harmonyrun feedback --no-attach

# 把 screen recording mp4 也带上
harmonyrun feedback --include-recording

# 演练模式：打印将执行的 gh 命令，但不真发
harmonyrun feedback --dry-run

# 显式调严重程度（low / medium / high；默认 medium）
harmonyrun feedback --severity high
```

### 10.1 前置条件

1. 安装 `gh`：<https://cli.github.com/>，然后 `gh auth login`。
   - 没装 `gh` 时命令仍可跑，但会回退到"浏览器打开预填表单"，trajectory zip
     留在 CWD，需要手动拖到 issue 评论里。
2. 仓库管理员把 OAuth token 配进 GitHub Secrets：

   ```bash
   # 本地一次性生成
   claude setup-token
   # 把输出粘到 repo Settings → Secrets → Actions:
   #   CLAUDE_CODE_OAUTH_TOKEN = <token>
   ```

   之后每次 Action 触发都从 Pro/Max 订阅扣额度，**不走 Anthropic API key**。

### 10.2 仓库落点与 drop-repo 模型

issue 和 trajectory zip **落在两个不同的仓**：

| 内容 | 落点 | 默认值 | env 覆盖 |
| --- | --- | --- | --- |
| issue（正文 + label + Claude 评论） | 主代码仓 | `HarmonyOS-AI/HarmonyRun` | `HARMONYRUN_GITHUB_REPO` |
| trajectory zip（release asset） | 专用 drop-repo | `HarmonyOS-AI/HarmonyRun-Feedback` | `HARMONYRUN_FEEDBACK_STORAGE_REPO` |

issue 仓 CLI 优先级：`HARMONYRUN_GITHUB_REPO` > `gh repo view` > `git remote get-url origin` > 默认值。zip 仓优先级：`HARMONYRUN_FEEDBACK_STORAGE_REPO` > 默认值。

**为什么分两个仓**：主仓是 private，测试人员通常只有最基本权限 —— 但「创建 release」需要 Write、「打 label」需要 Triage。把 zip 落到一个**专用 drop-repo**，就能给测试人员**只对 drop-repo 授 Write、对主仓授 Triage**：既能传 trajectory、又能触发 Claude，却**碰不到主仓代码**。数据全程留在 GitHub private，不外泄第三方。

release 用 **published（非 draft）**：drop-repo 本身 private，published 不会对外公开；但 workflow 用的跨仓只读 token 看不到 draft，所以必须 published。

**一次性配置（仓库管理员）**：

1. 建 private drop-repo `HarmonyOS-AI/HarmonyRun-Feedback`（带 README，保证有初始 commit，否则建不出 release tag）。
2. 建 team，对主仓授 **Triage**、对 drop-repo 授 **Write**，把测试人员加进去。
3. 建 fine-grained PAT（只勾 drop-repo 的 `Contents: Read-only`），存进**主仓** secret `FEEDBACK_REPO_TOKEN` —— `claude-on-feedback.yml` 用它跨仓下载 zip。
4. 主仓另需 `CLAUDE_CODE_OAUTH_TOKEN`（见 [§10.1](#101-前置条件)）。

fork 用户：把上面两个 env 一起改成自己的仓即可。

### 10.3 HTML 报告里的"📮 提交反馈"按钮

每个 case 的 `report.html`（CLI 跑完后写在 trajectory 目录下）右上角带这个
按钮。点击会弹出 modal 给出两条路径：

- 复制 `harmonyrun feedback <path>` 命令（推荐——完整 trace 上传 + 触发 Claude）
- 直接打开 GitHub 预填表单（兜底——没装 gh / 不在跑测试的机器上）

URL 预填使用 GitHub form 的 query param 协议，没有 trajectory 附件；
测试人员可在浏览器手工拖入 zip。

---

## 11. `memory`：跨执行记忆与 app 卡片学习

> **当前状态**：L0 自动记忆（写入/召回）已可用；`harmonyrun memory` 子命令（L1 候选生成/审阅/promote）的 CLI 入口**暂未开放**，代码已就位，后续合入时放开。

HarmonyRun 可以把**一次执行学到的经验**沉淀下来，下次跑**同一任务**时召回，注入到 system prompt 帮助模型少走弯路。能力分两层：

- **L0（per-case 记忆，自动）**：任务结束时把本次执行（指令 + 成败 + 失败信号 + 探索轨迹）蒸馏成一条「带结果标签的证据」笔记，按任务 key 存到 `~/.config/harmonyrun/memory/cases/`。batch 用例用 `test_case.id` 作 key（仅 test_execution 阶段写，precondition/postcondition 不写）；单跑用指令的归一化哈希作 key。下次同任务开局自动召回 top-N 注入。
- **L1（per-app 卡片，人工 review）**：把多个用例的 L0 记忆按 app 聚合，蒸馏成「候选 app 卡片知识」，经人工 review 后 promote 进 `app_cards`，走现有 app 卡片召回路径。

> **默认关闭，opt-in**：在 `config.yaml` 设 `agent.memory.enabled: true`（CLI override key 为 `memory_enabled`）才启用；`agent.memory.top_n`（override key `memory_top_n`，默认 5）控制召回条数。关闭时 system prompt 与改动前**字节一致**，不影响 prompt-cache。记忆始终以「历史证据，当前屏幕才是 ground truth」的口径注入，不会被当成指令盲从。蒸馏走可选的 `summarizer` LLM profile，缺失时回退到已加载的 agent LLM。

| 子命令 | 作用 |
|---|---|
| `harmonyrun memory generate-candidates [--app PKG] [--min-cases N]` | 聚合 L0 记忆，按 app 蒸馏候选卡片。`--app` 只处理指定包名；`--min-cases` 跳过记录数不足 N 的 app（默认 1）。 |
| `harmonyrun memory list` | 列出待 review 的候选卡片（app + 来源用例数 + 预览）。 |
| `harmonyrun memory promote <PKG> [--overwrite]` | 把候选卡片写入 `app_cards`。默认拒绝覆盖人工已有卡片；`--overwrite` 显式替换。 |
| `harmonyrun memory forget <KEY>` | 删除某任务累积的记忆（清理已知带毒的 key）。 |

所有子命令支持 `--config <path>` 指定 `config.yaml`。

---

## 12. `view`：查看测试报告

`harmonyrun view` 接受任意层级的 trajectory 目录，自动识别类型（单 case / 套件），用**当前包内最新模板**重新渲染 HTML 后在系统默认浏览器中打开。

```bash
# 自动找 ./trajectories/ 下最近一次运行
harmonyrun view

# 直接打开某次 run 的单 case 报告
harmonyrun view trajectories/20260528_142319_a8f4b2c1/

# 打开某次 test 的套件报告
harmonyrun view trajectories/MySuite/test_20260528_a8f4b2c1/

# 传 suite 根目录，自动选最近一次运行
harmonyrun view trajectories/MySuite/

# 传根目录，自动选最近一次（run 或 test 均可）
harmonyrun view trajectories/
```

**目录类型自动识别规则：**

| 目录内容 | 识别结果 |
| --- | --- |
| 含 `trace.jsonl` | 单 case 轨迹 → 渲染 case 报告 |
| 含 `report.json`（无 `trace.jsonl`） | 套件运行目录 → 渲染套件报告 |
| 两者均无 | 向下最多两层搜索，取最近修改的轨迹 |

**设计说明：**

- HTML 模板只存在于包内，不随 trajectory 数据一起分发。每次 `view` 都用**当前安装版本**的模板重新渲染，升级工具后旧 trajectory 也能得到新版查看器。
- trajectory 格式当前为 `1.0`（定义在 `src/harmonyrun/traces/format.py`），后续出现不兼容变更时会 bump 版本号并在 `view` 输出中提示。
- case 详情页与套件报告右上角均有「📁 打开目录」按钮：点击在系统文件管理器（Finder / 资源管理器 / xdg-open）中打开对应的本地目录（case 详情页打开该轨迹目录，套件报告打开套件根目录），快速查看原始文件（`trace.jsonl` / `meta.json` / `log/*.json` / 截图 / `report.json`）。走本地服务的 `/__open__` 端点（前端发页面自身相对目录、服务端经 `_resolve_open_target` 校验在目录内后调系统命令打开），仅在 `view` 服务下显示；直接 `file://` 双击离线打开时自动隐藏。

---

## 13. `hypium`：把跑通的用例转成 Hypium 回归脚本

`harmonyrun hypium generate` 读取一次 `test` 套件运行的 trajectory,把**成功**的用例转换成可独立执行的 [Hypium](https://gitee.com/openharmony/testfwk_arkxtest)(HarmonyOS 官方 UI 测试框架)Python 工程——录制一次,之后回放**零模型成本**,用于版本间的质量看护;版本更新导致回放失败时,再回到 agent 重新录制。

```bash
# 从套件运行目录生成 Hypium 工程(默认输出到 <目录>/hypium_project/)
harmonyrun hypium generate trajectories/MySuite/test_20260705_130321_d9aea6b3/

# 只转换指定用例(显式点名可覆盖"跳过失败用例"的默认行为)
harmonyrun hypium generate <套件目录> --case tc_001

# 指定输出目录 / 不生成 TestSuite 汇总文件
harmonyrun hypium generate <套件目录> -o ./my_hypium --no-suite

# 执行生成的工程(需要 pip install hypium)
harmonyrun hypium run <工程目录> [--case tc_001] [-d <serial>]
```

**转换规则:**

| 环节 | 机制 | 是否用 LLM |
| --- | --- | --- |
| 元素定位 | 记录时每步落盘 `ui_states/*.json` 结构化快照,坐标反解为稳定选择器,优先级 `BY.key(resourceId)` > `BY.text(text)` > `BY.type(...).isAfter(BY.text(锚点))` > 比例坐标兜底 | 否 |
| 动作映射 | `trace.jsonl` 的 `DeviceActionEvent`(tap/swipe/input_text/press_key/start_app/…)→ Hypium API;batch 的 setup+preconditions → `setup()`,test_execution → `process()`,postconditions+teardown → `teardown()` | 否 |
| 断言生成 | 用 `expected_result` + 执行段前后 UI diff 生成 `check_component_exist` 等断言 | 可选(生成时一次;无 LLM 时走规则) |
| 代码润色 | Step 注释、合并冗余滑动、断言归位 | 可选(生成时一次) |
| 回放 | 官方 hypium runner 独立执行 | 否 |

**注意:**

- 默认**只转换 `success=true` 的用例**——失败运行的动作序列不构成值得回放的质量基线;`--case` 显式点名可强制转换(用于排查)。
- 旧版 trajectory(无 `ui_states/` 目录)自动降级为比例坐标回放,仍可执行但选择器稳定性差;用当前版本重跑一次套件即可获得语义选择器。
- LLM 断言/润色使用 `llm_profiles` 里的 `hypium` profile(缺省回落 `fast_agent`);两者都是**生成时一次性**调用,生成产物的回放不消耗任何模型资源。
- 回放失败有两种含义:UI 合法变化(选择器/断言过时,应重新录制)或真实回归(App bug)。**不要**把失败无脑自动重录,先人工确认属于哪一种。

---

## 14. 常见问题

1. **`harmonyrun` / `python3 -m harmonyrun` 找不到**
  请确认使用的是安装了 wheel 的那个 Python。未激活虚拟环境时，使用 `.venv/bin/python3 -m harmonyrun`。
2. **`ModuleNotFoundError: langchain_google_genai` 等可选 provider 依赖**
  Google Gemini / Ollama 等 provider 需要额外安装：`pip install 'harmonyrun[google]'` / `'harmonyrun[ollama]'` / `'harmonyrun[all]'`；OpenAI / Anthropic 内置，无需额外装。
3. **401 / 403 / 模型不可用**
  检查用户配置目录下的 `.env`（路径见 [4.2](#42-用户配置目录)）中密钥是否有效；并确认 `llm_profiles` 的 `provider` 与 `model` 与该密钥匹配。
4. **找不到设备**
  先执行 `hdc list targets` 确认设备可见，再 `harmonyrun devices`；必要时 `harmonyrun doctor` 进一步诊断。
5. **运行时报 uitest 相关错误**
  Layout 取数路径已无 `ui_backend` 开关：socket 优先，失败一次就粘性熔断回 shell `uitest dumpLayout`。先跑 `harmonyrun doctor` 诊断；socket 失败可参考其建议执行 `scripts/diagnose_uitest_socket.py`。
6. **`hypium run` 失败**
  单独安装 Hypium 相关依赖与 CLI，确保 `python -m hypium` 等命令可用。

