Installation
npx @deepseek-ai/dsh plugin --profile web add dsh-mobile-remoteThis command is generated from the GitHub repository address. Inspect the upstream README and source before running it; pin a release or commit when reproducibility matters.
README
Maintainer-authored documentation snapshot.
dsh-mobile-remote
Your phone's WeChat becomes the remote control for your DeepSeek Harness agent.
Scan a QR code to bind a WeChat bot, then drive your dsh agent from WeChat over any network (4G / any WiFi): send commands, assign tasks, receive results, browse directories with /ls — plus two-way file transfer, proactive notifications, and multi-session switching.
- Full-permission mode: the agent can use every tool without confirmation (a
strictescape hatch is reserved viapermissionMode) - Voice messages: never stored or transcribed; replies「无法识别语音消息」(voice message not recognized)
- Messages transit Tencent iLink servers (WeChat has no third-party API) — not end-to-end encrypted; see "Risks & Boundaries"
Why this plugin
Compared with other dsh WeChat bridges (dsh-weixin, dsh-chatnode-wechat, dsh-im-bridge, …):
- Protocol fidelity: every wire detail was verified line-by-line against Tencent's official openclaw-weixin SDK v2.4.6 (notes and reference sources in
docs/) — outboundaes_keyencoding, the two separate media-type numbering schemes, and the CDN upload/download flows all match the official SDK. Both inbound AES key encodings (base64 of raw bytes / base64 of hex) are supported, with explicit errors on bad keys (no silent truncation) - Two-way file transfer: inbound images/files/videos are downloaded, decrypted and saved automatically (stable naming to prevent crash-replay duplicates + plaintext MD5 verification + 100 MB cap); outbound via the
/sendcommand or a[[send-file:path]]line in the model's reply — both share a single allowlist path check (symlink escapes, out-of-root paths and directories are rejected) - Proactive notifications: a
weixin_sendmodel tool (text + file) plus completion notifications for unbound sessions (rule-based dedup against double-send). Expired session tokens (-14) return an exact message, the health panel degrades and prompts a re-scan, and re-scanning while running hot-rotates credentials without a restart - Multi-session remote control:
/sessionslisting +/switchwith number/title dual semantics; when a session is taken over by another chat, the previous chat is notified — no cross-window output mixing - Reliability engineering (rare among peers): an exactly-once processing pipeline (queue + cursor atomically persisted), an at-least-once delivery outbox with crash recovery, a global processing limiter (8) + download semaphore (3) + backpressure throttling, drain re-entrancy mutexes and cursor identity checks, redundant credential persistence; 182 automated tests green, strict typecheck, and a zero-value-import gate for host packages
- Observability: a
/healthendpoint (allowlisted fields, no internal identifiers), a three-state status bar on the login page (running / missing credential / stopped + reason), and agateway.logfile log (1 MB rotation, 0600) - Clear security boundaries: the allowlist is the only usage boundary, the login page is loopback-only, error texts never leak keys or paths, and
weixin_sendfile sending is constrained by the same path allowlist as/send
Install
# 1. Install the plugin into the web profile
npx @deepseek-ai/dsh plugin --profile web add dsh-mobile-remote
# 2. Start (or restart) dsh web
dsh web
QR login
- With
dsh webrunning, openhttp://127.0.0.1:3080/mobile-remote-weixin/loginin a browser on this machine - Scan the QR code with WeChat and confirm (if WeChat shows a numeric code, enter it on the page)
- Once confirmed, message the bot from WeChat
The login page shows a gateway status bar (green = running / yellow = missing credential / red = stopped + reason); GET /mobile-remote-weixin/health returns a health snapshot (loopback-only, no internal identifiers).
Commands
| Command | Effect |
|---|---|
/status | Session state + session id + workspace |
/new | Unbind current session; next message starts a new one |
/stop | Stop the running task |
/reply <text> | Follow up on the current task |
/sessions | Last 10 sessions (current binding marked ⭐) |
/switch <number-or-title> | Switch session: pure digits resolve by /sessions number first, fall back to title match |
/切换聊天窗口:<title> | Always match by title (use this for titles that are pure digits) |
/send <file-path> | Send a file from the workspace/inbox to WeChat |
/ls [path] | List a computer directory |
/workspace | Show the current workspace |
/help | Show the command list again |
File transfer
- Inbound: images/files/videos sent from WeChat are downloaded, decrypted and saved to
<workspace>/.wechat-inbox/<date>/(name = original + message hash, stable naming prevents crash-replay duplicates;maxMediaBytescap, 100 MiB default). The model sees a[received file] <absolute path>hint and can continue with vision tools. - Outbound:
/send <path>sends directly; the agent can also put a single[[send-file:path]]line in its final reply (the line itself is never shown to the user). Paths must be inside the workspace or the inbox directory (one shared check; symlink escapes and out-of-root paths are rejected). - Voice messages stay rejected: not downloaded, not stored.
Proactive notifications
weixin_sendtool: agents in WeChat sessions can push text/files to WeChat (to the bound chat window; falls back tonotifyChatIdwhen unbound). Disable withenableWeixinSendTool: false.- Completion notifications: configure
notifyChatId+notifyOnTurnEnd: true, and when a task finishes in a session not bound to WeChat, a✅ Task complete: session「title」push goes to the notification target (bound sessions are never double-notified; deduped when the tool already pushed this turn).
Configuration (all optional)
Environment variables:
| Variable | Default | Meaning |
|---|---|---|
WEIXIN_BOT_TOKEN | none | Login token (auto-saved after QR login; normally not needed) |
WEIXIN_ALLOWED_USERS | the scanner | Allowed user ids, comma-separated |
WEIXIN_ALLOWED_GROUPS | empty | Allowed group ids (groups need user+group match) |
WEIXIN_BOT_API_BASE | https://ilinkai.weixin.qq.com | iLink gateway |
WEIXIN_CDN_BASE | https://novac2c.cdn.weixin.qq.com/c2c | Media CDN |
WEIXIN_MAX_MESSAGE_CHARS | 3500 | Reply chunk length |
WEIXIN_MAX_MEDIA_BYTES | 100 MiB | Media size cap |
WEIXIN_PERMISSION_MODE | full | full = full permission; strict = reserved escape hatch |
WEIXIN_DSH_WORKSPACE | auto | Default workspace for new WeChat sessions (this var > explicit config > dsh current workspace > process cwd) |
cordis config keys:
| Key | Default | Meaning |
|---|---|---|
inboxDir | '' (=workspace/.wechat-inbox) | Inbox directory; out-of-root values fall back with a warning |
enableWeixinSendTool | true | Master switch for the weixin_send tool |
notifyChatId | '' | Notification target / tool fallback (Web sessions can also push once set) |
notifyOnTurnEnd | false | Push completion notifications for unbound sessions |
logDir | '' (=state dir) | Gateway log directory (gateway.log, 1 MB rotation) |
statePath | ~/.dsh/mobile-remote-weixin/gateway-state.json | Gateway state file |
Health & logs
- Health snapshot (
/health): running state, poll activity, consecutive error count, binding counts, backlog, credential presence, start-failure reason — allowlisted fields only, never chat/session ids or progress. - File log:
gateway.log(append-only, 0600, 1 MB single-generation rotation, silent degradation on write failure), tee'd to the dsh logger.
Risks & boundaries (please read)
- Full-permission mode: no human in the loop; the allowlist is the only boundary — only add your own WeChat account.
weixin_sendoutbound surface: the tool is visible to in-scope agents; injected inbound messages could convince the model to push workspace text/files to WeChat.filePathis constrained by the same allowlist as/send, but text content is not path-checked — do not use in untrusted groups.- Privacy: messages transit Tencent iLink servers (not end-to-end encrypted); decrypted media lands in the workspace
.wechat-inbox. - Account coexistence: driving the same WeChat account with another iLink client (e.g. OpenClaw) will steal messages — disable one of them.
- Credentials: the token is stored locally (credentials service + managed fallback file, 0600); a leak equals account control.
- Platform risk: iLink is an undocumented bot API that may drift; rule-breaking use risks a ban — at your own risk.
Limitations & not-supported (honest disclosure)
- No voice recognition: voice messages always get「无法识别语音消息」(not downloaded/transcribed/stored) — a deliberate design decision
- No multimodal pipeline: images/files are only saved to disk with a path hint for the model; the plugin does not parse content (pair with a vision tool such as
vision_analyze) - No scheduled tasks: no cron-style "do X every day"
- No remote approval buttons: full-permission mode means no human-in-the-loop confirmations and no approve/reject interaction in WeChat;
strictmode is a reserved escape hatch (approval flow not implemented) - Depends on an undocumented protocol: iLink may drift and break features; misuse risks a ban
- Single account binding: one credential set per dsh instance; multiple WeChat accounts need multiple profile instances (no built-in multi-instance management)
- No content-level file dedup: resending the same file in different messages lands duplicate copies (only crash replays of the same message are deduplicated)
- Chinese-only commands and copy: slash commands, help text and prompts are Chinese; no English/i18n
- No graphical settings panel: all configuration is via environment variables / cordis patch (the login page only offers QR scan, a status bar and the health endpoint)
- Requirements: Node ≥ 22.12, a dsh web profile, and a WeChat account that can scan-bind an iLink bot
Development
npm install
npm run typecheck
npm test
npm run build
License
MIT
Project files and signals
Shown items are public repository signals detected in the directory snapshot.
Repository information
- Language
- TypeScript
- License
- MIT
- Last updated
- Aug 16, 2026, 7:18 AM
Install deliberately
Review source code, permissions, lifecycle hooks, dependencies and network access. Test untrusted plugins in an isolated environment.