# @fmode/wechat-cli (`wecli`) Local CLI wrapping the third-party **wechat-agent** HTTP backend for use by the **`wechat` OpenClaw skill** (out-of-band WeChat operations: search contacts, look up conversation history, message a third party, etc.). **This CLI is the non-channel half of the WeChat integration.** The real-time inbound/outbound auto-reply path belongs to the [`channel-plugin/`](../channel-plugin) sibling (`@fmode/openclaw-wechat-agent`, currently at 0.1.1). See [design notes](#why-a-cli-and-not-more-skills). | | Channel plugin (`../channel-plugin`) | CLI + skill (this) | |-|-|-| | Lives in | OpenClaw gateway, always on | Spawned per agent tool call | | Handles | "Someone DM'd the bot, auto-reply" | "User asks agent to actively do something" | | Uses | 3 endpoints (check-online, /messages, /send-text) | ~8 endpoints today; growing | | Deployed as | `.tgz` via `openclaw plugins install` | local binary + `SKILL.md` | ## Status - **v0.1.0** — local-only (not published to npm). Covers `check-online`, `contacts list|search|detail`, `messages send|history`, `conversations list`. Zero runtime dependencies (Node 20+ `fetch` + `util.parseArgs` only). ## Quick start ```bash # One-time cd cli npm install npm run build # Option A — run directly node dist/wecli.js --help node dist/wecli.js check-online --api-base http://8.138.37.248/api/wechat-agent # Option B — make `wecli` a global command on this machine npm link wecli --help wecli check-online ``` `npm link` is reversible with `npm unlink -g @fmode/wechat-cli`. On Windows it writes a `.cmd` shim into `%APPDATA%\npm`; on Unix it symlinks into the active Node's `bin` dir. ## Config resolution Every command resolves the backend `apiBase` using this priority chain; the first hit wins: 1. `--api-base ` CLI flag 2. `WECHAT_API_BASE` environment variable 3. `~/.wecli/config.json` → `{ "apiBase": "..." }` 4. `~/.openclaw/wechat-credentials.json` → `{ "wechatApiBase": "..." }` _(legacy; pre-dates this CLI; shared with the v1.x skill pack so existing OpenClaw installs keep working without extra setup)_ If none of the above yield an `apiBase`, the command errors out with a multi-line message pointing at all four locations. To make things explicit for this machine, the simplest setup is: ```bash mkdir -p ~/.wecli printf '{"apiBase":"http://8.138.37.248/api/wechat-agent"}\n' > ~/.wecli/config.json wecli check-online # should print `online yes` ``` ## Subcommand reference ### `wecli check-online` ``` apiBase http://8.138.37.248/api/wechat-agent source flag online yes selfWxid - ``` - Exit code `0` when online, `3` when reachable but offline, `1` on error. - `--json` → `{"ok":true,"online":bool,"selfWxid":string|null,"apiBase":...,"apiBaseSource":...}`. ### `wecli contacts list [--cache]` Pulls all contacts (`POST /contacts/list` or `/contacts/list-cache` when `--cache` is given). Output is a simple table (`wxid | nickName | remark | alias`). Use `--json` for agent consumption. ### `wecli contacts search ` Keyword search via `POST /contacts/search`. The query string is sent as both `keyword` and `query` fields because the upstream GEWE variant differs between backend builds. ### `wecli contacts detail ` Single-contact detail via `POST /contacts/detail`. Prints `nickName`, `remark`, `alias`, `signature`, `type`. `--json` returns the raw backend record so the agent can grab any extra fields the terminal view drops. ### `wecli messages history [--wxid W] [--since ISO] [--direction D] [--limit N]` Server-side filtered & deduped via `GET /messages`. | flag | maps to | default | |-|-|-| | `--limit N` | `limit=N` (capped at 200 server-side) | 50 | | `--direction received\|sent\|all` | `direction=...` | `received` | | `--wxid wxid_xxx` | `wxid=...` | _(all contacts)_ | | `--since 2026-04-21T16:00:00Z` | `since=...` | _(no lower bound)_ | `--since` is the recommended way to poll: backend returns only messages newer than that timestamp and already deduplicates by `NewMsgId`/`MsgId`. The human-format footer also reports `server total=N, server dropped K dup` so you can see how much filtering happened upstream. ### `wecli messages send --to --text "" [--at wxid1,wxid2]` Active send via `POST /message/send-text`. **Use this only for messaging a third party** — replies in an ongoing DM with the bot are handled by the channel plugin automatically; you do NOT need to call `messages send` from the agent to reply to incoming inbound. If you do, the channel plugin will still also reply and the contact will see duplicates. `--at` fills the backend's `ats` field (comma-separated wxids) for group `@mention` replies. ### `wecli conversations list` `GET /conversations`. Returns the backend's per-contact "last message" summary grouped by `wxid`. Useful as a pre-check for "who have I been talking to lately". ## Why a CLI and not more skills OpenClaw's canonical pattern (see `imsg`, `wacli` SKILL.md under `node_modules/openclaw/skills/`) is: - **One binary** (`imsg` / `wacli` / `wecli`) does the HTTP / RPC. - **One SKILL.md** tells the LLM when to use which subcommand. The alternative — one SKILL.md per endpoint with inline `curl` examples, as this repo's v1.x `wechat/wechat-*` folders still do — creates a maintenance tax: every command re-derives `apiBase` from a JSON file inline (`node -e "console.log(JSON.parse(fs.readFileSync...))"`), LLMs have to hand-escape JSON / query strings, and adding a feature means creating a new skill directory. `wecli` replaces that boilerplate with one stable arg-parsing surface the agent can learn once. ## Development ```bash npm run typecheck # tsc --noEmit, strict TS npm run build # esbuild single-file bundle -> dist/wecli.js (~80 KiB) ``` ### Adding a new subcommand 1. Pick the matching backend endpoint from `e:\workspace\wechat-agent\wechat-agent-api\src\routes\wechat-agent.ts`. 2. Add a typed method to `WechatAgentClient` in `src/client.ts`. 3. Create `src/commands/.ts` following the shape of `check-online.ts` (resolveConfig → client → render human / `--json`). 4. Register the command in the `switch` in `src/index.ts` and add a line to the `USAGE` string. 5. `npm run build && node dist/wecli.js --api-base ` to smoke test. ### Out of scope (on purpose) The wechat-agent backend exposes 116 endpoints. `wecli` **intentionally does not wrap**: - Moments / `/sns/*` (19 endpoints) — social ops, separate product surface. - Video-channel / `/finder/:name` — niche. - `/contacts/corp-*` (企微) — enterprise WeChat, distinct channel. - `/contacts/phone-*` — privacy / compliance. - Destructive group ops (`/group/create`, `/disband`, `/admin-operate`, `/remove-member`) — need human-in-the-loop. - Profile management (`/profile/update`, `/profile/head-image/upload`, `/profile/safety-info`) — belongs to the web UI, not agent tooling. If you genuinely need one of these from the agent, package a separate skill + binary (e.g. `wecli-moments`) rather than bloating the core tool.