# Mushroom Forecast APP 控制技能

此目录可整体拷到另一台电脑，无需原仓库。含技能正文、26 个动作参考、Python 标准库客户端与 mock 测试；不安装 pip/npm 依赖。适用于 Claude Code、Codex 等支持 SKILL.md 的 Agent。

## 安装

将整个 `mushroom-forecast` 目录复制到目标电脑（不是只复制 SKILL.md），选择对应 Agent 的位置：

```bash
# 在包含 mushroom-forecast 目录的文件夹执行
mkdir -p ~/.claude/skills
cp -R mushroom-forecast ~/.claude/skills/

# Codex 用户技能目录（如已有同名目录，请先确认再替换）
mkdir -p ~/.agents/skills
cp -R mushroom-forecast ~/.agents/skills/
```

重新开启 Agent 会话，确认识别 `mushroom-forecast`。Codex 的项目技能也可放 `.agents/skills/`；旧版/自定义安装若使用不同技能目录，以其配置为准。对 Agent 说“操作蘑菇地图 APP”或“规划采蘑菇时间和地点”。

## 前置条件与配对

- Python **3.7+**（只用标准库）。手机安装已支持接口的 APP，与电脑在同一可互访的局域网（无客户端隔离），或可互访的 VPN（如 Tailscale）。多地址 / VPN 隧道入口需使用包含该功能的新版本。
- 用户在 APP 设置页打开“允许 Agent 控制 App”，授予系统需要的本地网络权限。APP 保持前台；相机/筛选/聚焦须在地图页且引擎已就绪；closeDetail 和清单查询只要求前台。
- 设置页列出全部成功监听的局域网地址和 VPN 隧道上的 100.64.0.0/10 内网地址（如 Tailscale），标签分别为“局域网”和“VPN / Tailscale”（不含蜂窝网卡的运营商 CGNAT）。其它 VPN 若使用同一地址段和匹配的隧道网卡名也会被开放，入口仍需 token。复制或二维码内容为每行一个 URL，末行 `Authorization: Bearer <token>`；所有监听共享一个 token，任选一个可达 URL 填入 `GUIJI_APP_URL`。二维码不加地址标签，自动增加 QR 版本以保留全部地址；多地址扫码可读性待真机验收。
- token 仅由用户通过环境变量提供，不放进文件、日志、命令参数或 URL。重置 token 在全部地址上立即撤销旧凭据；关闭开关关闭全部监听；重启 APP 后需要重新配对。切 Wi-Fi 或 VPN 断开重连后关闭再打开开关，重新扫描网卡。
- 推荐在启动 Agent 的同一终端安全输入 token，使其子进程继承环境。下面命令适用于 Bash/Zsh，实际 token 不出现在命令历史或终端回显中：

```bash
export GUIJI_APP_URL='http://192.168.1.20:8765'  # 或 http://100.64.0.8:8765；替换为设置页一个实际可达地址
printf 'APP token（隐藏输入）: '
IFS= read -r -s GUIJI_APP_TOKEN
printf '\n'
export GUIJI_APP_TOKEN
```

不要启用 `set -x`、打印环境变量或保存含 token 的终端记录。值仅为原始 token，不含 `Authorization: Bearer `。此接口是内网明文 HTTP，勿暴露到公网或用于不可信网络。控制结束后 `unset GUIJI_APP_TOKEN`，并在 APP 关闭开关。

鉴权请求（state 或 command）到达后，手机在 APP 前台期间保持常亮，地图右侧第一个用户按钮变蓝，提示“Agent 已连接”。只有局域网 token 缺失、错误或已撤销导致的 401 不计入连接；token 通过后，即使命令返回 400（参数错误、未知动作）或上游服务返回 401/403，仍算 Agent 已连接（连接表示 Agent 在通信，不表示命令成功）。120 秒无鉴权请求、关闭开关、重置 token 或服务销毁后恢复原按钮和系统默认息屏，退后台也会释放常亮。

## 最小连通测试

进入已安装技能目录（下面以 Claude Code 为例；Codex 替换为其安装位置）：

```bash
cd ~/.claude/skills/mushroom-forecast
python3 scripts/guiji_app.py state
python3 scripts/guiji_app.py command account.status
```

state 成功输出 JSON 状态本身；command 成功输出含 ok/result/settled/state 的对象。失败退出 1，stderr 保留 HTTP 状态和原错误正文（含 code、reason 及附加字段）。不会把 token 写入输出，也不走环境代理、不跟随重定向、不自动重试。客户端等待上限 30 秒，断连/超时不保证动作未执行。

任意支持的动作使用 `command <action> --params '<JSON对象>'`，省略 params 时发送 `{}`。请求参数见 [references/api.md](references/api.md)；日期/地点/范围/物种缺项时按 SKILL.md 的默认规则自行推断补全并直接展示结果，只在连不上、定位与地点都缺、同名地点选错会跑偏、扩大到非公共土地、未提过的外部导航/收藏这几类必要情形才问用户一句。对用户回话保持简短口语，不汇报动作名和技术字段。

查目标物种先用 `species.list`（id/name/aliases），林型用 `forestTypes.list`；两者只要求前台，不改地图。地图动作（相机与筛选）会自动关闭当前林块详情或图层面板、自动隐藏用户设置页回到地图，比较下一林块直接 `map.focusPatch` 即可；被其它页面挡住时仍须先人工关闭。聚焦观察 10 秒，相机/筛选仍为 5 秒；`settled:false` 可一次短等后只读确认，不自动重发。`favorites.list` 顶层 `offline` 为 true/false/null，空缓存和未知来源均不能当作云端无收藏；`queued`/`initiated` 分别只代表待同步/已发起。

## 地址未知：可选 Bonjour 发现

Bonjour 仍只在局域网广播，不通过 VPN 隧道广播。VPN 调用方请从设置页取得标为“VPN / Tailscale”的地址直连，不依赖自动发现。

不需 token，不发送 HTTP 请求；使用系统已有工具，最多运行 5 秒，不安装新依赖：

```bash
python3 scripts/guiji_app.py discover
# macOS：按浏览输出中的实际服务名解析，不一定恰好叫 Guiji Agent
python3 scripts/guiji_app.py discover --name 'Guiji Agent'
```

服务类型 `_guiji-agent._tcp`。macOS 用 dns-sd 输出的主机名和端口；Linux 用 avahi-browse 的解析输出（通常 `=` 行）。确认是用户的手机后设置 `GUIJI_APP_URL='http://<主机或IP>:<端口>'`。多台手机先问用户；发现结果可能有系统缓存，不能证明开关仍开启。没有工具/无输出/失败时请用户手动给设置页地址，token 始终另从设置页获取。

## 本地 mock 测试（拷到另一台电脑也能跑）

在本技能目录执行，不需手机或真实 token：

```bash
python3 -B -m unittest discover -s tests -v
```

覆盖本地 mock HTTP 的 state、任意 command、请求头/JSON、完整错误透传、HTTP 200 业务失败、301/302/303/307/308 重定向拒绝、禁用环境代理、无自动重试、输入校验；另测发现工具降级/限时调用和脚本单独复制后运行。测试 token 是虚构值。

## 验收边界

- Python mock 与动作一致性检查不等于手机验证。
- **待办（用户）**：另一台电脑连手机做真机端到端；验证复制/二维码配对、Bonjour、state、意图推断（缺项不追问）→ topPatches → patches → 直接展示最佳林块 → 下一林块（自动关上一详情）→ 已授权收藏/导航，权限不足、锁定数据、not_ready 和 token 撤销。
- 采集前另核实天气、道路与区域准入。野生蘑菇需由有资质的人鉴定，规划结果不是鉴定依据，不可凭此食用。
