# Hermes Agent 在 Termux(Pixel 10 Pro Fold)上的安装记录 记录 2026-06-25 在一台新 root 的 Pixel 10 Pro Fold(codename `rango`,build `CP1A.260405.005`)上从源码(`/Users/yibo/Documents/lemonAI/hermes-agent`,非 `git clone`)装 Hermes Agent v0.17.0 到 Termux 的完整经验。按踩坑顺序记录,最重要的坑放最前面。 ## 0. 最容易再犯的坑:macOS tar 打包必须排除 AppleDouble 文件 **现象**:`hermes chat` / `hermes doctor` 报 `UnicodeDecodeError: 'utf-8' codec can't decode byte 0xa3 in position 45`,看起来像是 Android 平台兼容性问题,浪费了大量时间去查 locale / 编码环境。 **真因**:在 Mac 上用 `tar -czf src.tar.gz .` 打包源码时没设 `COPYFILE_DISABLE=1`,macOS 的 BSD tar 会把每个文件的 Finder 扩展属性打包成同名的 `._xxx` AppleDouble 二进制 sidecar 文件一起塞进 tar 包。推到设备解压后,仓库里混进了 **6289 个 `._*` 文件**。`tools/registry.py: discover_builtin_tools()` 扫描 `tools/*.py` 时把 `tools/._xxx.py` 也当源码用 `read_text(encoding="utf-8")` 读,二进制 blob 当然解码失败。 **修法**: ```bash find . -name '._*' -type f -delete # 在已污染的目录里清 # 或者打包时从源头避免: COPYFILE_DISABLE=1 tar -czf src.tar.gz --exclude='.git' . ``` **教训**:以后任何从 macOS 往 Linux/Android 环境传源码(tar/zip 都一样),打包命令前一定先 `export COPYFILE_DISABLE=1`,或者直接用 `git archive`(不会带 AppleDouble)。 ## 1. 自动化执行 Termux 命令的方式:`adb shell run-as`,不是真正打开 App 这台设备 root 后,`adb shell run-as com.termux ` 可以直接以 Termux App 自己的 UID 跑命令,不需要打开 Termux 的 Activity/Service,也不需要 `RUN_COMMAND` intent + `allow-external-apps` 配置。坑点: - `run-as` 不会加载 Termux 的 profile,必须手动 `export HOME=/data/data/com.termux/files/home PREFIX=/data/data/com.termux/files/usr PATH=$PREFIX/bin:$PATH`。 - `run-as` 读不到 `/sdcard`(scoped storage 限制),文件要先 `adb push` 到 `/sdcard/Download/`,再用 **root(`su -c`)** 复制进 `/data/data/com.termux/files/home/`,并且必须手动修正属主和 SELinux context,否则 Termux 进程自己访问不了: ```bash su -c "cp /sdcard/Download/x /data/data/com.termux/files/home/x" su -c "chown u0_a329:u0_a329 /data/data/com.termux/files/home/x" su -c "chcon u:object_r:app_data_file:s0:c73,c257,c512,c768 /data/data/com.termux/files/home/x" # 类别(category)要跟目录一致,否则 mlsconstrain 拒绝访问 su -c "chmod 700 /data/data/com.termux/files/home/x" ``` - 多层 shell quoting(本机 bash → adb shell → run-as → sh -c)极易出错。稳妥做法:**把命令写成脚本文件再推上去执行**,不要塞 inline 一长串带引号的命令。 - 设备充电(`AC powered: true`)时 Doze 不会触发,息屏不影响后台跑的编译/安装。 ## 2. psutil==7.2.2 在 Android 上无法从源码构建 **现象**:`pip install -e '.[termux]'` 卡在 `Failed to build 'psutil'`,报 `platform android is not supported`。换了 psutil 5.9.8~7.2.2 之间一串版本号都一样报错。 **真因**:psutil 的 `setup.py` 按 `sys.platform` 字符串选择要编译哪份平台相关 C 源码(`psutil/_common.py: LINUX = sys.platform.startswith("linux")`)。CPython 3.13 在 Termux 上把 `sys.platform` 正确地报成 `'android'`(而不是 `'linux'`),导致 `LINUX` 判断为 `False`,落进 psutil 的"不支持"分支退出。 **修法**:不要试图 patch psutil 源码自己编译。Termux 官方仓库已经维护了预编译的 `python-psutil` 包,版本恰好对得上: ```bash pkg install -y python-psutil python -m venv --system-site-packages venv # 关键:让 venv 继承系统 site-packages ``` `--system-site-packages` 让 venv 里 `import psutil` 直接复用系统包,pip 解析依赖时发现已满足 `psutil==7.2.2`,不会再去源码构建。 **附带验证**:`psutil.cpu_percent()` 之类读全局 `/proc/stat` 的调用在 Android 上会 `PermissionError`(系统级限制,非 bug),但 hermes 实际只用 `psutil.pid_exists()` / `Process(pid).status()` 这类只读自身/子进程的调用,完全正常。 ## 3. jiter(Rust/PyO3 扩展)编译的两个连续坑 `jiter` 没有 Android wheel,必须现场用 `maturin` 编译。`pyproject.toml` 没钉版本但需要联网拉一整棵 Rust 依赖树。 ### 3a. cargo 并行编译的 `Text file busy` 竞态 **现象**:`error: failed to run custom build command for proc-macro2` → `Text file busy (os error 26)`。 **修法**:`export CARGO_BUILD_JOBS=1` 降并发后重试即可,是构建脚本并发写/执行同一路径的瞬时竞态,跟 Android/交叉编译无关。 ### 3b. 镜像的同步缺口 **现象**:配置中科大 `mirrors.ustc.edu.cn/crates.io-index` 镜像后,`wasip2`/`wasip3` 两个 WASI 相关小 crate 连续两次下载超时(`failed to transfer more than 10 bytes in 30s`)。 **修法**:删掉 `~/.cargo/config.toml` 里的镜像配置,直连官方 `crates.io`(`static.crates.io` 实测 0.85s 拿到 200,比想象中快很多)。**不要想当然认为国内必须用镜像**——先测一下直连速度,crates.io/PyPI 的 CDN 在这个网络环境下表现完全不差,镜像反而可能有特定包同步不全的问题。 **注意**:pip 的清华源(`pypi.tuna.tsinghua.edu.cn`)全程很稳,没遇到类似问题,只有 cargo 的镜像踩了坑。 ### 关键加速点:pip 的 wheel cache maturin 自身编译一次要 10+ 分钟(单线程,编译 470+ 个 crate)。pip 成功后会把 maturin 的产物缓存成 wheel(`~/.cache/pip/wheels/.../maturin-1.14.1-cp313-cp313-android_24_arm64_v8a.whl`),**之后每次 `pip install` 重试都会复用这个 wheel,不会重新编译**——所以中途换源/调参数后放心重跑整条 `pip install -e '.[termux]'` 命令,不会从零开始。 ## 4. ffmpeg 包安装失败:libc++ 符号版本不一致 **现象**:`pkg install ffmpeg` 装到一半报 `CANNOT LINK EXECUTABLE "ffmpeg": cannot locate symbol "_ZNSt6__ndk1..."`,是 Termux 仓库里这批包之间 libc++ 版本暂时不同步导致的。 **修法**:直接 `pkg upgrade -y`(会顺带升级 70 多个基础包,包括 libc++ 本身),再 `dpkg --configure ffmpeg` 收尾,问题消失。不是要单独重装 ffmpeg。 ## 5. Kimi Coding Plan key 的配置方法:别跑 `hermes setup` `hermes setup` / `hermes model` 是 `prompt_toolkit` 交互式向导(方向键选择 + 文本输入),在没有真实 PTY 的 `adb shell run-as` 自动化通道里没法驱动。改为直接写配置文件,效果完全等价: `~/.hermes/.env`: ``` KIMI_API_KEY=sk-kimi-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ``` `~/.hermes/config.yaml`: ```yaml model: default: "kimi-for-coding" provider: "kimi-coding" ``` `sk-kimi-` 前缀的 key 默认就走 `https://api.kimi.com/coding/v1`(Kimi Code API / Coding Plan),不需要额外设 `KIMI_BASE_URL`。只有 `platform.moonshot.ai` 发的老 key 才需要覆盖 base_url。 **验证方法**:`hermes doctor` 的 "Auth Providers" 段会出现 `✓ Kimi / Moonshot` 连通性检查;更直接的端到端验证是非交互单次问答: ```bash hermes chat -q "你是谁,用的是哪个模型?" ``` `-q` 是一次性查询模式(问完直接退出,不进交互循环),自动化场景测试模型是否真的能跑通就用这个,不要用裸的 `hermes chat`(会卡在交互式 TUI 里)。 ## 已知的、不影响使用的剩余警告 - `tirith security scanner enabled but not available` —— tirith 试图下载 `aarch64-unknown-linux-gnu` 版本的二进制(glibc,不是 Termux 的 Bionic),不会真正可用,hermes 已优雅降级为纯 pattern matching,不阻塞功能。 - `Plugin 'wecom-platform' ... dlopen failed: cannot locate symbol "PyExc_Warning"` —— 本地编译的 `cryptography` Rust 扩展在加载某个可选插件时有 C-API 符号问题,8/49 个插件未启用,核心 chat 功能不受影响。 - `Context file AGENTS.md TRUNCATED` —— AGENTS.md 超过默认 context 截断阈值,是内容长度问题,跟安装无关。