dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
Turn your phone's Huawei XiaoYi into a remote control for the DSH agent on your own PC. DSH (DeepSeek Harness) host plugin for XiaoYi OpenClaw mode: zero third-party deps, hand-written RFC6455 WebSocket client, AK/SK auth, heartbeat, A2A streaming, inbound attachments, outbound files.
English | 简体中文
A DSH (DeepSeek Harness) host plugin that connects Huawei XiaoYi in OpenClaw mode to your local DSH session — voice, text and images from your phone reach the agent running on your own machine.
This is not a standalone chatbot. XiaoYi is only the entry point; what it connects to is your local DSH session (same tools, same working directory). There is no second agent.
Huawei XiaoYi on your phone (anywhere)
│ voice / text / images
▼
Huawei Cloud wss://hag.cloud.huawei.com/openclaw/v1/ws/link
│ long-lived connection (AK/SK auth + 30s heartbeat)
▼
This plugin (runs inside the DSH host process)
│
▼
DSH session xiaoyi-listen-*
This is not plug-and-play. Before installing, make sure you have all three:
| # | Requirement | Notes |
|---|---|---|
| 1 | A DSH host | DSH = DeepSeek Harness. This is a host-side plugin; without a host it cannot run. |
| 2 | XiaoYi Open Platform credentials (OpenClaw mode) | You need AK / SK / agentId. These are Huawei Cloud developer credentials you must apply for yourself on the XiaoYi Open Platform. |
| 3 | Node.js ≥ 20 | The code uses the built-in fetch / FormData / Blob. Developed on Node 24. |
If you cannot obtain item 2, you can still read this repository as a protocol reference implementation —
index.jsimplements the XiaoYi/OpenClaw handshake, auth, heartbeat and JSON-RPC message exchange in full.
AK / SK / agentId come from?They are issued by Huawei. This project cannot create them for you and cannot tell you whether your account is eligible. You have to apply for them yourself:
agentId — shown in the AgentCard block;AK / SK — under Credentials → New credential. The SK is displayed only once, so copy it immediately.Menu names, entry points and the review process are decided entirely by Huawei and may change at any time. If a step above no longer matches what you see, follow Huawei's own documentation rather than this file. This plugin only consumes the three credentials. It never asks Huawei for anything else on your behalf.
clearContext / tasks/cancel).read_image / read).| Direction | Support |
|---|---|
| Phone → this plugin (what you send to XiaoYi) | Text, voice (transcribed) and images/files all work. Attachments are downloaded to ~/.dsh/xiaoyi/media/ and their local paths are handed to the agent. |
| This plugin → phone (what you get back in XiaoYi) | Text works — the reply body, plus anything sent with xiaoyi_send. Pushing an image or file back to the phone is unreliable: the protocol accepts only a public https URL, and the XiaoYi client is not guaranteed to render the file part. Treat xiaoyi_send_photo as experimental. |
In short: the inbound direction is the reliable one. If your workflow depends on images or files arriving on your phone, do not rely on it.
Put this directory anywhere you keep plugins. The conventional location is:
~/.dsh/plugin-sources/dsh-xiaoyi
Option A (recommended, one command):
# install from the GitHub repository
dsh plugin --profile web add git+https://github.com/HangZhouXi/dsh-xiaoyi
# or from npm (after it is published there)
dsh plugin --profile web add dsh-xiaoyi
Option B (edit by hand):
Edit ~/.dsh/profiles/web/package.json:
{
"dsh": {
"profile": {
"bundles": [
// ...
"dsh-xiaoyi" // ← add to bundles
]
}
},
"dependencies": {
// ...
"dsh-xiaoyi": "git+file:///C:/Users/<you>/.dsh/plugin-sources/dsh-xiaoyi"
}
}
A plain local path dependency also works:
"dsh-xiaoyi": "file:../../plugin-sources/dsh-xiaoyi".
dsh plugin --profile web install
Then cold-start the DSH host. (Host-side plugins are not applied by hot reload — a cold start is required.)
Edit ~/.dsh/xiaoyi/state.json:
{
"enabled": true,
"ak": "XiaoYi Open Platform AK",
"sk": "XiaoYi Open Platform SK",
"agentId": "agentId of your OpenClaw-mode agent",
"wsUrl": "wss://hag.cloud.huawei.com/openclaw/v1/ws/link",
"sessionId": "",
"cwd": "C:\\Users\\<you>\\Desktop",
"publicBaseUrl": "",
"imageHost": ""
}
| Field | Purpose |
|---|---|
enabled | Master switch. false disconnects the channel. |
ak / sk / agentId | XiaoYi Open Platform credentials. All three are required. |
wsUrl | WebSocket endpoint. Leave as the default unless Huawei changes it. |
sessionId | The DSH session to reuse. Normally written by the plugin itself. |
cwd | Working directory for the session. Must be writable. |
publicBaseUrl | Only for outbound files in local mode: the public https base URL of your tunnel. |
imageHost | Optional default publisher for outbound files. Set "catbox" to make catbox.moe the default; leave empty to stay local-only. |
You can also let the agent configure these at runtime with the xiaoyi_set tool (changes take effect immediately and reconnect).
Log file: ~/.dsh/xiaoyi/xiaoyi.log
| Tool | Purpose |
|---|---|
xiaoyi_status | Channel status: whether credentials are complete, whether the WebSocket is connected, the listening session id, queue length, last error, message counters. |
xiaoyi_set | Update credentials / switch (ak, sk, agentId, wsUrl, enabled). Takes effect immediately on the live connection. |
xiaoyi_send | Push a short message to the current XiaoYi conversation (useful as a long-task completion notice). |
xiaoyi_send_photo | Send a local image or file to XiaoYi (the protocol only accepts public https; see "Outbound files" below). |
xiaoyi_reconnect | Force a disconnect and reconnect (after changing credentials, or when the link looks stale). |
| Path | Purpose |
|---|---|
GET /xiaoyi/state | JSON status. |
GET /xiaoyi/connect | Start the channel and make sure the listening session is ready. |
GET /xiaoyi/disconnect | Stop the channel. |
GET /xiaoyi/public/<filename> | Serves published outbound files. Filenames carry a random component and are not guessable. |
Verified against the published artifacts of
@ynhcj/xiaoyi. This project does not depend on that package.
| Stage | Detail |
|---|---|
| Endpoint | wss://hag.cloud.huawei.com/openclaw/v1/ws/link |
| Auth headers | x-access-key=AK, x-sign=Base64(HMAC-SHA256(SK, ts)), x-ts=milliseconds, x-agent-id=agentId |
| Handshake | {"msgType":"clawd_bot_init","agentId":"..."} |
| Heartbeat | Every 30s {"msgType":"heartbeat","agentId":"...","timestamp":...} plus a protocol-level ping |
| Inbound | JSON-RPC 2.0, method="message/stream"; sessionId=params.sessionId, taskId=params.id, messageId=top-level id; text in params.message.parts[kind=text].text, attachments in parts[kind=file].file{name,mimeType,uri} |
| Outbound | {"msgType":"agent_response", agentId, sessionId, taskId, msgDetail:"<JSON-RPC string>"} |
| Outbound frame kinds | result.kind="status-update" (acknowledge with state=working), result.kind="artifact-update" (the body, closed with lastChunk=true, final=true) |
| Control | clearContext / tasks/cancel / action="clear" → reply with the matching JSON-RPC response |
xiaoyi_send_photo)The Part sent back to the client is { url, filename, mediaType } — only public https URLs are accepted; there is no "upload bytes" interface. Hence two modes:
| mode | Behaviour | Privacy |
|---|---|---|
local (default) | The file is copied to ~/.dsh/xiaoyi/public/ and served by this plugin's own route; pair it with a tunnel (e.g. localhost.run) and point state.publicBaseUrl at it. | The file never leaves your machine (except through your tunnel). |
catbox | Uploads to the third-party host catbox.moe. | The file is sent to a third party. Must be requested explicitly. |
local mode requires state.publicBaseUrl; without it the tool returns an error and never silently falls back to an image host.
~/.dsh/xiaoyi/state.json, which is excluded by .gitignore. They are never committed and never logged (the log only records whether a value is present).uri comes from an external message and is treated as untrusted input. Only http/https is allowed. Every hop of a redirect chain is checked before it is fetched (redirect: 'manual'), and each host is judged two ways: structurally, and by what it actually resolves to. IPv6 addresses are parsed into bytes rather than compared as strings, so equivalent spellings cannot slip through. Blocked ranges: loopback, private, link-local, unique-local, multicast, site-local, IPv4-mapped (::ffff:a.b.c.d), NAT64, 6to4 and Teredo — including cloud metadata endpoints such as 169.254.169.254.content-length cannot exhaust memory./xiaoyi/state and /xiaoyi/public/* are reachable by anyone who can reach the port. Do not expose the DSH port directly to the internet; if you use a tunnel, prefer one with authentication or an unguessable hostname.media/ directory is never cleaned automatically. Prune ~/.dsh/xiaoyi/media/ yourself periodically.dsh-xiaoyi from bundles and dependencies in ~/.dsh/profiles/web/package.json.dsh plugin --profile web install~/.dsh/xiaoyi/ (it contains credentials and logs).| Symptom | What to check |
|---|---|
| Nothing happens after installing | Did you cold-start? Host-side plugins are not applied by hot reload. |
status reports ready:false | One of AK / SK / agentId is missing. |
Cannot connect, lastError mentions a handshake failure | Is wsUrl correct? Can the machine reach hag.cloud.huawei.com:443? |
| Connects then keeps reconnecting | Look for "stale connection" in the log; if nothing (including pong) arrives for over 90 seconds the plugin reconnects on purpose. |
| XiaoYi messages get no reply | Check ~/.dsh/xiaoyi/xiaoyi.log and confirm in the host log that the plugin loaded. |
| Reply says the DSH session is not ready | The very first request after a host start has to create the session; send the message again shortly after. |
| Sent images do not arrive | This direction is unreliable by design — see "What can travel in each direction". The protocol also requires a public https URL, so check publicBaseUrl and whether your tunnel is still alive. |
Please read DISCLAIMER.md before use.