# agent 调用方式 ## 必备工具 - HTTP 客户端:agent 自带 `web_fetch` / `curl` / Node 22 `fetch` 任一 - Node 22:执行 `scripts/pospal.mjs`(Linux / macOS 自带 `node` 或 NVM) - 隐藏式输入:Linux/macOS `bash read -rs`、或 Node `readline` 关闭回显 - 单次请求硬性超时:`AbortSignal.timeout(12000)`(12 秒) ## 凭证管理 ### 首次获取(agent 主动询问) ```bash printf "PSPLVISITORID: " >&2 IFS= read -rs PSPAL_VISITOR_ID printf "\n" >&2 ``` 旧版 `bash` 不支持 `-s` 时: ```bash stty -echo printf "PSPLVISITORID: " >&2 read PSPAL_VISITOR_ID stty echo printf "\n" >&2 ``` ### 注入到 helper 调用 ```bash export POSPAL_VISITOR_ID="$PSPAL_VISITOR_ID" node scripts/pospal.mjs build-booking-body < continuous-slots.json ``` helper 也支持 `--visitor `,但**优先用 env**——避免值出现在进程列表 (`ps aux`)。 ### Session 结束 ```bash unset POSPAL_VISITOR_ID ``` 新 session 必须重新索取,不允许持久化。 ## helper 子命令 | 子命令 | 作用 | 何时用 | |---|---|---| | `parse-availability` | 把 list 端点原始 JSON 转结构化 slots(含状态判定、活动合并) | 每次拿到 list 响应后必跑 | | `find-continuous` | 从 slots 中找符合 `--start HH --end HH` 的完整连续组合 | 拿到结构化 slots 后 | | `build-booking-body` | 从连续组合生成锁场请求体 | 锁场前 | | `parse-booking-response` | 解析锁场响应,**剥离 `script.paySign`** | 锁场后 | | `fingerprint` | 访客凭证 → 12 字符 hex 指纹 | 日志关联 | | `redact` | 任意 JSON → 移除敏感字段 | 落盘或汇报前 | 所有子命令从 stdin 读 JSON(除 `fingerprint`),都支持 `--help`。 ## 工作流示例 ### 单次查询 ```bash # 1. 调 list 端点 RAW=$(curl -sS -X POST 'https://wxservice-stg48.pospal.cn/wxapi/AppointmentVenue/LoadValidClassRoomApptSettingV2' \ -H 'Accept: */*' \ -H 'APPTYPE: 1' \ -H 'Content-Type: application/json' \ -H 'PSPLVISITORAUTO: API' \ -H 'Referer: https://servicewechat.com/wx2088ca46b47d8be7/11/page-frame.html' \ -H 'STOREID: 6038652' \ -H 'VERSIONINFO: NC|2026.07.03' \ -d '{"dateTime":"2026-07-16","userId":6038652,"projectUid":"1775556129325750199"}') # 2. 解析 echo "$RAW" | node scripts/pospal.mjs parse-availability --date 2026-07-16 # 3. 找连续组合 echo "$RAW" | node scripts/pospal.mjs parse-availability --date 2026-07-16 \ | node scripts/pospal.mjs find-continuous --start 19 --end 21 ``` ### 锁场 ```bash # 1. 重新查一次(不复用旧缓存) RAW=$(curl -sS -X POST '...' -d '...') # 2. 解析 + 找连续 + 构造请求体 BODY=$(echo "$RAW" | node scripts/pospal.mjs parse-availability --date 2026-07-16 \ | node scripts/pospal.mjs find-continuous --start 19 --end 21 \ | node scripts/pospal.mjs build-booking-body) # 3. 调锁场 RESP=$(curl -sS -X POST 'https://wxservice-stg48.pospal.cn/wxapi/AppointmentVenue/SaveVenueAppointmentV2' \ -H "PSPLVISITORID: $POSPAL_VISITOR_ID" \ -H @write-headers.txt \ -d "$BODY") # 4. 解析并脱敏 echo "$RESP" | node scripts/pospal.mjs parse-booking-response --slots continuous-slots.json ``` `write-headers.txt` 模板见 `references/protocol.md` 末尾。 ### 监控循环(agent 自行实现) ``` for date in today, today+1, ..., today+6: slots = curl list(date) → parse-availability cont = find-continuous(slots, target_start, target_end) key = (date, cont.txtClassroomUid, cont.startTime, cont.endTime, sum_price) if cont and key not in seen: notify(cont) seen.add(key) elif not cont and key in seen: notify("removed: {key}") seen.discard(key) sleep 10 ``` 整点(如 `19:00:00.000`)agent 主动绕过冷却立即查一次。同一 date 的并发查询只允许一个在飞。 ## 失败处理速查 | 现象 | agent 动作 | |---|---| | 列表返回 `successed: false` | 视为该日无可订数据,不重试 | | 列表返回缺字段 | 视为接口变更,停止对该球场所有操作,告诉用户"接口字段变化,暂停服务" | | 锁场 `successed: false` | 换时段最多重试 1 次,仍失败则停下 | | 锁场 HTTP 5xx / 网络错误 | 不重试,告诉用户"结果不确定,请去小程序核实" | | 锁场后 `script.expireTime` 已过 | 锁场已失效,告知用户 | | `enrollSlots` 含目标时段 | 该时段按已订处理,不计入连续组合 | | `PSPLVISITORID` 未索取 | 写操作前必须先向用户隐藏式索取;缺则停下 | ## 调试与脱敏 ```bash # 任何写操作前,确认 visitor 指纹 node scripts/pospal.mjs fingerprint "$POSPAL_VISITOR_ID" # → "a3f9b21c0e4d" (12 字符) # 落盘任何响应前必须 redact curl ...list... | node scripts/pospal.mjs redact > list-redacted.json # 看 helper 自检 node scripts/pospal.mjs parse-availability --date 2026-07-16 \ < evals/fixtures/availability-sample.json ``` 完整 `list-headers.txt` / `write-headers.txt` 模板见 `references/protocol.md` 末尾的 curl 示例。