๐ฑ DSH Feishu Remote
> Control your DeepSeek Harness agent from Feishu / Lark on your phone. Send a task by DM โ get the result back in chat. Fully working closed loop.
  
โจ What it does
- ๐จ Feishu โ Agent โ Feishu: DM your bot a task (e.g.
2+3็ญไบๅ ๏ผ), a DSH agent runs it with your configured LLM, and the answer comes back to the chat. - ๐ผ๏ธ Image understanding: send a screenshot or photo โ it's downloaded and analyzed by a vision model (Qwen3-VL), then the agent replies with what it sees.
- ๐จ Image generation round-trip: the agent can generate an image (
generate_image), download it locally (feishu_download), and send the actual image back to your Feishu chat (feishu_send_image) โ not just a link. - ๐ค Agent โ Feishu: the model gets
feishu_send/feishu_send_image/feishu_downloadtools to push results, files, and generated images to any user or chat. - ๐งน Retention cleanup: downloaded images are kept for 7 days (configurable via
FEISHU_IMG_RETENTION_DAYS), then auto-deleted โ no unbounded disk growth. - ๐ค Long connection: uses
lark-cli's WebSocket event bus โ no public webhook server needed, works on localhost/LAN/private servers. - ๐ Secure: reuses
lark-cli's OS-keychain credential storage and permission system; event listener runs unsandboxed by design (it must hold the WebSocket).
๐ Install
0. Prerequisites
1. A Feishu/Lark self-built app with: - Bot ability enabled - Event subscription im.message.receive_v1 (long-connection mode) - Permissions: im:message, im:message:send_as_bot, im:message.p2p_msg:readonly, im:chat:read, im:resource - A published version - (Setup in the Feishu developer console โ the CLI can enable the bot ability via API, but events/permissions need the console.)
2. lark-cli installed & authenticated once:
npm i -g @larksuite/cli
lark-cli config init # paste your App ID / Secret
lark-cli auth login --recommend # scan QR to authorize1. Install the plugin
dsh plugin --profile demo add github:ShiXiangYu2/dsh-feishu-remoteThe bundle contains two pieces:
index.jsโ the Cordis plugin: registers thefeishu_sendmodel tool and attempts an in-process event listener.feishu-resident.mjsโ the recommended resident launcher: boots the web profile and runs the long-connection event loop in a detached process (see below).
2. Configure the model
The DSH profile must have a working LLM route (e.g. DeepSeek via SiliconFlow):
# profile cordis.patch.yml
- id: llm-deepseek
config:
apiKeyEnv: SILICONFLOW_API_KEY
baseURL: https://api.siliconflow.cn/v1
thinking: disabled
reasoningEffort: off
models:
- id: deepseek-ai/DeepSeek-V3.2
name: DeepSeek-V3.2 (via SiliconFlow)
contextWindow: 65536
maxTokens: 8192
- id: agent-default-model
config:
provider: deepseek-official
model: deepseek-ai/DeepSeek-V3.23. Run the resident (recommended)
The closed loop must live in a long-lived process. Use the included resident launcher:
# Adjust the absolute paths in feishu-resident.mjs (LARK_HOME, CLI) to your setup.
DSH_HOME=~/.dsh SILICONFLOW_API_KEY=sk-... \
node --import tsx/esm feishu-resident.mjsIt boots the web profile, spawns lark-cli event consume as a detached process (holding stdin open via a tail -f /dev/null pipe so the listener never exits on EOF), and for each inbound DM: ack โ create agent โ run task โ extract final text โ reply.
4. Use it
DM your Feishu bot anything, e.g. ๅธฎๆๆป็ปไธไธ ~/projects ็ README โ the agent runs and the result comes back to the chat.
๐ Tools
| Tool | Description |
|---|---|
feishu_send | Model-facing: send a message to a Feishu user (ou_) or chat (oc_). |
feishu_send_image | Model-facing: send a local image file to a Feishu user or chat. |
feishu_download | Model-facing: download a URL to a local file (so generated images can be sent via feishu_send_image). |
๐ How it works
Feishu DM โโโบ lark-cli event consume (WebSocket long-connection, detached process)
โ NDJSON event on stdout
โผ
feishu-resident.mjs (long-lived process)
โ image? โ download (messages-resources-download)
โ โ vision describe (Qwen3-VL via SiliconFlow)
โ agents.create + followup(task) + whenIdle()
โผ
final assistant text (ev.data.message.content)
โ lark-cli im +messages-send
โผ
Feishu chat replyImage handling
- The event content for an image arrives as
[Image: img_v3_xxx]. - The resident detects that pattern, downloads the resource via
lark-cli im +messages-resources-download (using the event's real message_id + the image_key), then asks a SiliconFlow vision model (Qwen/Qwen3-VL-8B-Instruct, overridable with FEISHU_VISION_MODEL) to describe the picture.
- The description is prepended to the user's message and fed to the DSH agent,
so the agent can reason about the image and reply in Feishu.
- Downloaded images land in the
IMG_DIR(/root/dsh /feishu-imagesby
default; the download command requires a relative --output path, so the resident cds into that directory first).
โ ๏ธ Notes
- Why a resident process? DSH's shell service binds background processes to the calling plugin fiber; a listener started inside a plugin's
apply()is killed when the fiber settles. The resident launcher owns the listener in its own process, so it survives. - Text extraction: the final answer is read from the session log's
assistant/messageevents (ev.data.message.content, mirroring the official headlesssummarize()). - Long tasks: replies are truncated to the final text block; very long runs may exceed Feishu message limits.
- lark-cli event output streams as NDJSON on stdout (stderr carries
[event]log lines) โ both are parsed.
๐ License
MIT