Part of the DSH ecosystem — channel plugins built on the DeepSeek Harness (DSH) plugin architecture.
Feishu (Lark) IM channel bridge for the DeepSeek Harness (DSH) — turn dsh into a Feishu bot with one long-lived Agent per chat (true rolling memory, flushed to disk).
eventsmode (default): consumesim.message.receive_v1directly vialark-clion the same machine. Requires this machine to own the app's single global event-bus connection, andlark-cliauthenticated as the bot (lark-cli auth login).relaymode: long-polls a public HTTP job relay, so the DSH machine can sit behind NAT. A Feishu-side bot (e.g. an OpenClaw/Clawdbot agent, or any webhook forwarder) POSTs user tasks to the relay; this bridge claims jobs, answers with the host's default model, and POSTs replies back. Both sides connect outbound only.
Both modes share the same core: per-chat resident Agents, serialized turns per chat, event deduplication, exponential-backoff self-healing, group-chat @-mention gating (events mode), and reply-length capping.
# 1. Put the bundle somewhere DSH can resolve it, e.g. into the runtime:
cp -R dsh-feishu ~/.dsh/runtime/node_modules/dsh-feishu
# (out-of-tree: any profile resolves bundles from the dsh install anchor,
# or expose it via ~/.dsh/profiles/node_modules/ symlink)
# 2. Create a profile ~/.dsh/profiles/feishu/package.json:
# {
# "name": "dsh-profile-feishu",
# "private": true,
# "dependencies": {},
# "dsh": { "profile": { "bundles": ["@deepseek-ai/dsh-base", "dsh-feishu"] } }
# }
# plus empty cordis.yml (`[]`) and pnpm-workspace.yaml.
# 3. Add your deployment config (see below) to
# ~/.dsh/profiles/feishu/cordis.patch.yml
# 4. Boot:
dsh --profile feishuThe bundle itself is environment-agnostic — all deployment values live in your profile's user patch layer:
- id: feishu-bridge
config:
mode: events # or: relay
# --- relay mode ---
# relayBaseUrl: 'https://<your-relay-host>/openapi/relay'
# relayApiKeyEnv: DSH_FEISHU_RELAY_KEY # key read from this env var at boot
# --- shared ---
workspace: ~/.dsh/feishu-channel/workspace
mentionOnlyInGroups: true
minBackoffSeconds: 5
maxBackoffSeconds: 120
maxReplyChars: 3800The relay API key is never stored in config files — export it before boot:
export DSH_FEISHU_RELAY_KEY=... # then: dsh --profile feishurelay mode needs a public HTTP endpoint implementing five routes
(a reference implementation as a Miaoda/Feishu 妙搭 full_stack NestJS app is
easy to assemble; auth via Authorization: Bearer <key> at the gateway):
| Route | Who calls | Purpose |
|---|---|---|
POST /ask {chat_key, text, wait_seconds} |
Feishu-side bot | Submit a task, long-poll the reply |
GET /poll?wait=25 |
this bridge | Claim pending jobs |
POST /reply {job_id, reply} |
this bridge | Return the Agent's answer |
GET /result/{job_id} |
Feishu-side bot | Re-check after an ask timeout |
GET /health |
anyone | Liveness |
- A Feishu custom app with bot capability; subscribe event
im.message.receive_v1; grantim:message.p2p_msg:readonly(+ send scopes for the bot). lark-cliinstalled and authenticated (lark-cli auth login, bot identity ready).- Only ONE event bus may connect per app globally — stop any competing consumer (e.g. a cloud OpenClaw instance using the same app) first.
- Inbound image/voice/video/file messages are downloaded (via
lark-cli im +messages-mget --download-resources) into<workspace>/media/and the Agent is told the local path. - Outbound, the Agent emits
[[fsfile:/absolute/path]]markers; the bridge routes images as image messages, everything else as file attachments. - Relay mode stays text-only in v0.2 (extend the relay job schema if you need media there).
- One Agent per
chat_id(events) /chat_key(relay), created lazily on first message, memory lives for the process lifetime; sessions persist to the DSH session store (~/.dsh/sessions). - Group chats are only answered when the message @-mentions the bot
(set
mentionOnlyInGroups: falseto answer everything). - Model: whatever the host's default model selection is
(
agent-default-modelin~/.dsh/settings.yaml).
License: MIT-style, do whatever you like; no warranty.