Bundle
dsh-feishu-remote
Control your DeepSeek Harness agent from Feishu/Lark: send tasks via DM, receive results back, approve tool calls from mobile cards. Powered by lark-cli.
- Source
- ShiXiangYu2
- stars
- 1 stars
- License
- MIT
- Updated
- Updated 14 days ago
Readme
# ๐ฑ DSH Feishu Remote
> Control your [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) agent from **Feishu / Lark** on your phone. Send a task by DM โ get the result back in chat. **Fully working closed loop.**
[](https://github.com/topics/dsh-plugin) [](https://github.com/deepseek-ai/deepseek-harness) [](LICENSE)
## โจ 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_download` tools 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](https://open.feishu.cn/app) โ the CLI can enable the bot ability via API, but events/permissions need the console.)
2. `lark-cli` installed & authenticated once:
```sh
npm i -g @larksuite/cli
lark-cli config init # paste your App ID / Secret
lark-cli auth login --recommend # scan QR to authorize
```
### 1. Install the plugin
```sh
dsh plugin --profile demo add github:ShiXiangYu2/dsh-feishu-remote
```
The bundle contains two pieces:
- `index.js` โ the Cordis plugin: registers the `feishu_send` model 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):
```yaml
# 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.2
```
### 3. Run the resident (recommended)
The closed loop must live in a **long-lived process**. Use the included resident launcher:
```sh
# 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.mjs
```
It 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 reply
```
### Image 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-images` by
default; the download command requires a **relative** `--output` path, so the
resident `cd`s 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/message` events (`ev.data.message.content`, mirroring the official headless `summarize()`).
- 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
Install
dsh plugin --profile web add github:ShiXiangYu2/dsh-feishu-remote
Profile: web
With the hub plugin installed, ask your agent to install it by name โ it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-feishu-remote from the hub
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.