@peng272/dsh-wechat-ilink
v0.10.2
Published
WeChat ClawBot (Tencent iLink) channel for DeepSeek Harness 0.2.0-rc.2 — talk to DSH from WeChat.
Readme
@peng272/dsh-wechat-ilink
WeChat ClawBot (Tencent iLink) channel for DeepSeek Harness 0.2.0-rc.2 — talk to DSH from your WeChat chat window.
WeChat ──→ iLink gateway ──→ outbound long poll ──→ DSH session ──→ reply ──→ WeChat
(no public IP, no domain, no port forwarding)Install
dsh plugin --profile <profile> add @peng272/dsh-wechat-ilinkThe package name is scoped. The unscoped
dsh-wechat-ilinkon npm is a different, older package (0.6.5) by another maintainer — installing that one does not install this code.
The bundle inserts one row (wechat-clawbot) and its patch carries the default
config. Nothing else is required: on first start, with no account bound, the
channel requests a login QR code. The credential is stored under
<DSH_HOME>/clawbot/ and reused on every later start.
Where the login QR appears
No window opens. The QR is delivered two ways, and both are files rather than a popup:
| Where | What |
| --- | --- |
| <DSH_HOME>/clawbot/login-qr.png | the scannable image — open it and scan with WeChat |
| <DSH_HOME>/clawbot/channel.log | the same code as terminal ASCII art (QR ▀▄… lines), plus the raw login URL |
So a first run that "does nothing" has almost always already written
login-qr.png. Check that file first. If the QR rendering itself fails, the raw
login URL is still logged — rendering failure never blocks login.
To drive the login by hand instead:
dsh-wechat-ilink loginIt prints the QR to the terminal as well, so it works even when nothing is watching the log.
Upgrading from 0.6.x
Nothing to do. The runtime identities are unchanged, so an in-place upgrade keeps
your config, your bound account, and your message cursor. See
CHANGELOG.md for the one list that matters: which config keys are
currently accepted-but-inert (typing, acceptImages).
CLI
dsh-wechat-ilink login # scan a QR code to bind a WeChat account
dsh-wechat-ilink status # binding state and cursor
dsh-wechat-ilink logs [n] # durable log tail
dsh-wechat-ilink logout # unbind all accountsRuns standalone — no DSH runtime needed. Equivalent long form:
node lib/cli.js status.
Configuration
- id: wechat-clawbot
name: '@peng272/dsh-wechat-ilink'
config:
enabled: true
sessionId: 'wechat-clawbot'
cwd: 'C:\path\to\your\workspace'
allowedUserIds: []
typing: true
typingKeepaliveMs: 5000
maxMessageChars: 2000
acceptImages: true
progressNotice: true
turnTimeoutMs: 300000
selfTestOnStart: false
selfTestDelayMs: 3000
logLevel: info| Key | Default | Meaning |
| --- | --- | --- |
| enabled | true | Master switch. false loads the bundle without connecting. |
| sessionId | wechat-clawbot | The DSH session the conversation is bound to; reused across restarts. |
| cwd | workspace default | Working directory for the bound session. |
| provider / model / reasoningEffort | DSH default | Accepted; currently the headless profile decides the route. |
| allowedUserIds | [] | Extra WeChat ids allowed to drive the agent. Empty means only the account that scanned the QR may talk to the bot. |
| typing / typingKeepaliveMs | true / 5000 | Accepted but not implemented in 0.7.0. |
| maxMessageChars | 2000 | Outbound chunk size in characters. |
| acceptImages | true | Accepted but not implemented in 0.7.0; image messages are ignored. |
| progressNotice | true | Send a short "received, working on it" notice while a turn runs. |
| turnTimeoutMs | 300000 | Abort a turn that runs longer than this. |
| selfTestOnStart | false | Run one synthetic turn at startup and log the outcome. |
| logLevel | info | silent | error | info | debug. debug logs inbound text. |
Extensions beyond the 0.6.x schema: baseUrl, stateDir, outgoingMaxPerSec,
outgoingBurst, disableBridge.
Where state lives
<DSH_HOME>/clawbot/
accounts.json index of bound account ids
accounts/<accountId>.json token, baseUrl, bound userId
accounts/<accountId>.sync.json get_updates_buf cursor
channel.log durable log
bridge.lock single-instance guard
login-qr.png last rendered login QR (transient)DSH_HOME defaults to %USERPROFILE%\.dsh.
Design notes
Single-instance guard
Two pollers on one bot steal messages from each other, silently. The channel
takes a lock at <stateDir>/bridge.lock recording the holder's pid:
- holder alive → this instance refuses to poll and logs the holder's pid;
- holder gone → stale lock, this instance takes over.
The failure this prevents is real: a bridge is typically a supervisor chain
(scheduled task → .cmd → node), and stopping the supervisor kills the outer
layers while the node grandchild survives as an orphan and keeps polling. A
fresh start then runs alongside it.
Shutdown removes only its own lock, so a successor's lock is never deleted.
Conversation driving
Each message runs dsh --profile headless --json --session-id <id> "<prompt>"
and parses the NDJSON it emits (session / final / status.turn_end).
--session-id gives the thread continuity.
One process per message, in exchange for not touching the in-process agent path
at all — which also means this plugin declares no inject and cannot be blocked
from activating by a missing service.
QR rendering
The gateway returns only a login URL, never an image:
https://liteapp.weixin.qq.com/q/xxxx?qrcode=<32 hex>&bot_type=3lib/qr.js renders it in-package: byte mode, versions 1–10, ECC L/M, its own PNG
encoder (1-bit greyscale, zlib), and a half-block terminal renderer. No runtime
dependency on a QR library, and no third-party service ever receives the login
credential.
Rendering failure never blocks login — the raw URL is always printed.
Known limits
- The gateway throttles sends: roughly 5–6 messages in a burst earns
ret=-2for about an hour, and retrying during the penalty makes it worse. The channel paces outbound sends (outgoingMaxPerSec, default 0.5/s) and chunks long replies to stay under it. Re-scanning resets the penalty but changes the bot identity. - Config is read at startup: restart the bridge after editing the profile patch.
typingandacceptImagesare accepted but inert in 0.7.0.
License
MIT
