Passer au contenu principal
A

dsh-lark-plus

anfruj/dsh-lark-plus

Passerelle entre DeepSeek Harness et Feishu/Lark : elle propose le chat, les cartes, les approbations, un tableau de tâches, des objectifs et des plannings dans Feishu, ainsi qu’une transcription vocale entièrement locale (hors ligne) avec une barre de lecture dans la Web GUI.

Installer

dsh plugin --profile web add github:anfruj/dsh-lark-plus

README

dsh-lark-plus mascot

🪶 dsh-lark-plus

DeepSeek Harness × 飞书/Lark 双向桥接
私聊 / 群聊 · 卡片按钮 · 审批与提问回传 · 任务板 · goal · 定时任务
外加:全程本机的语音转文字 + 网页端播放条

npm downloads MIT node bridge offline fork


中文

DeepSeek Harness × 飞书/Lark 双向桥接插件:把 DSH 智能体装进飞书——扫码建应用、30 秒上线,在飞书里对话、点卡片、回审批、跑任务、看任务板。在此基础上还带了一条全程本机的语音链路:飞书里按住说话,音频在本机由 SenseVoice 转成文字,并可直接在 DSH 网页端回放——音频不出本机、不调任何云端语音 API。

本仓库是 dsh-lark-link 的 fork(MIT,原作者及贡献者版权保留)。桥接部分沿用上游实现,语音链路为新增。详见文末归属与许可。

🌟 桥接能力(沿用上游)

  • 零门槛:/lark setup 扫码即建飞书应用,不用手搓开放平台、不用配回调、不用公网服务器
  • 零丢失:出站 Outbox + 入站 WAL 双持久化,进程崩溃 / 插件热更 / dsh 重启后消息与回答都补得回来
  • 零学习成本:切换类命令都是单选卡片,点一下即生效;DSH 原生命令直接用
  • 真 Agent:不是聊天机器人——bash / 文件 / 子代理 / 工作流全套工具,飞书里跑完整 Harness

✨ 特性

能力说明
🎙 本地语音转写飞书发语音 → 本机 SenseVoice 转文字(见上文);音频原文件始终保留
🎯 一键认证/lark setup 扫码创建飞书应用(自动订阅消息事件 + 群聊全量 + 表情权限 + 用户基本信息),30 秒上线;也支持 DSH_LARK_APP_ID/SECRET 手动通道
🧠 多模式 Agent标准 / PTC / 极简 / Cordis preset + 你在 GUI 自建的 preset,飞书发 /mode 出单选卡片即切
🎛 权限分级只读 / 工作区写 / Full access 三种权限,/permission 卡片即切
🎨 卡片化命令/mode /permission /model 全是单选按钮卡片——点一下即切换
💬 意图确认转发模型提问(ask_user_question)→ 飞书意图确认卡片,答完模型继续
😊 表情回执收到消息随机表情;回复完成 / 命令完成打 DONE ✅
💪 出站零丢失持久 Outbox(JSONL + at-least-once + 幂等键 + 分航道并行 + 周期清理)
🆕 入站请求补发Agent 处理到一半插件/dsh 崩溃,重启后自动重新触发该消息
🛡 连接自愈probe 驱动受控重连 + 配额熔断 + 断连补偿;环境代理自动规避
🔀 命令三级分流桥特有命令桥处理;DSH 注册命令原生执行;其余原样注入 Agent
📎 入站多媒体飞书图片 → 视觉模型看图;文件 → 有界文本提取;语音 → 本地转写
📤 出站多媒体模型经 lark_plus_send_local_file 主动回传本地图片/文件
🩺 一键诊断/doctor → ZIP 诊断包(含当前会话 session log + 脱敏配置)
✍️ Markdown 渲染回复自动检测 markdown → CardKit 卡片渲染,纯文本走文本消息
🌊 可选流式输出/lark-config streaming.enabled=true 热开流式卡片(默认关,省流量)
🖥 复用 DSH Web GUI桥 Agent = 原生 DSH session,聊天/工具卡/设置全由 GUI 呈现
👥 访问控制allowlist 白名单;groupPolicy 群聊触发策略;denyList 命令前缀拒绝兜底

🚀 快速开始

前置:Node.js ≥ 24,已安装 DeepSeek Harness。语音功能另需 ffmpeg(见上文)。

安装(DSH 官方 bundle 机制,产物已随仓库提交,不执行构建脚本):

# 从 npm 安装(推荐,已发布 0.1.0)
dsh plugin --profile <你的档> add dsh-lark-plus

# 从 GitHub 仓库安装(跟进尚未发版的提交用这条)
dsh plugin --profile <你的档> add github:AnFRuJ/dsh-lark-plus

# 或本地目录(源码开发时)
dsh plugin --profile <你的档> add link:/path/to/dsh-lark-plus

装完在 DSH 输入框执行一次扫码上线:

/lark setup      # 手机飞书扫码,自动创建应用并写入凭据
/lark start      # 建立长连接
/lark status     # 查看连接/会话/补发计数

之后在飞书里直接发文字、图片、文件,或按住说话发语音。

桌面版 DSH 的 desktop 档由 Electron 独占,CLI 会拒绝;那种情况请走 GUI 侧边栏「插件」页添加同一条地址。

🎙 附加能力:本地离线语音转文字(+ 网页端回放)

它是怎么工作的

飞书语音(OGG/Opus)
   ↓  下载(本机 → 飞书 API,唯一的网络请求)
   ↓  原文件落盘保留      ← 你随时能在飞书回听,agent 也能复读同一份文件
   ↓  ffmpeg 转 16 kHz 单声道 WAV
   ↓  SenseVoice(sherpa-onnx,CPU 推理)
   文字 → 作为这条消息的正文交给智能体
  • 引擎:SenseVoice(int8,中/英/日/韩/粤 + 自动标点),通过 sherpa-onnx-node 在本机 CPU 上跑。
  • 音频去向:只有一次联网 —— 下载约 228 MB 的模型。下载不在插件装载时触发(安装 / 激活 / 启动永远秒级):第一次收到语音消息、或在侧栏面板点「下载模型」、或调用 POST /dsh-voice-local/v1/model/download 时才开始。之后完全离线。
  • 失败不丢消息:模型没下好、ffmpeg 缺失、解码失败时,音频照样落盘,正文留空并把原因写进日志。同一条语音永远不会因为转写失败而消失。
  • 可关:配置里 voice.enabled: false 就只保留音频、不做转写。
  • 可回放:DSH Web UI 里每条语音正文下面直接有一条播放条(宿主只读路由 GET /plugins/lark-plus/audio?name=…,支持 Range),不用回飞书重听;原始附件卡片会被隐藏,一屏只剩「文字 + 播放条」。

前置条件

依赖说明
ffmpeg必须。飞书语音是 OGG/Opus,模型只吃 16 kHz WAV。默认按 PATH 查找,也认 voice.ffmpegPath / DSH_VOICE_FFMPEG。
模型装载时不会下载;首次需要转写时(或手动点「下载模型」/ 调 POST /dsh-voice-local/v1/model/download)才下到 $DSH_HOME/voice/sensevoice/(默认 ~/.dsh/voice/sensevoice/),与 dsh-voice-local 共用同一目录——装过那个插件、或已手动放置模型,都不会重复下载。可用 voice.sha256 / voice.modelSha256 / voice.tokensSha256 做完整性校验。

手动放置模型(内网 / 下载慢时):解压官方归档,保证目录里有 model.int8.onnx 和 tokens.txt 即可:

mkdir -p ~/.dsh/voice/sensevoice
# 官方:https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/
#       sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17.tar.bz2
tar -xjf sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17.tar.bz2 \
    --strip-components=1 -C ~/.dsh/voice/sensevoice

配置

# cordis.patch.yml(profile 的 patch 层)
- id: lark-plus
  config:
    voice:
      enabled: true
      modelDir: ''        # 空 = $DSH_HOME/voice/sensevoice
      ffmpegPath: ''      # 空 = 自动探测
      modelUrl: ''        # 主下载源覆盖
      mirrors: ''         # 逗号分隔,优先于内置镜像(hf-mirror / ghfast)
      sha256: ''          # 可选:模型归档 tar.bz2 的 SHA-256
      modelSha256: ''     # 可选:model.int8.onnx 的 SHA-256(直连文件源)
      tokensSha256: ''    # 可选:tokens.txt 的 SHA-256
      timeoutMs: 600000   # 可选:单次下载尝试上限(默认 10 分钟);慢源失败即换下一个
      ffmpegTimeoutMs: 60000

本地转写接口

语音链路把转写能力也开成了本机路由(可直接被其他工具复用;只服务本机):

方法路径说明
GET/dsh-voice-local/v1/health引擎 / 模型 / ffmpeg / 下载状态
GET/dsh-voice-local/v1/model/status模型文件与下载进度
POST/dsh-voice-local/v1/model/download触发后台模型下载
POST/dsh-voice-local/v1/transcribebody = 16 kHz 单声道 PCM16 WAV,返回 { ok, text }

自测

npm run test:voice      # 真实模型 + 真实 ffmpeg;资产缺失时自动 skip
npm run check           # tsc --noEmit

📖 完整文档

下面保留上游的中文文档(命令、卡片、诊断、配置项等),其中的命令名已同步为 /lark。


⌨️ 命令

DSH 侧(GUI 或终端)

/lark setup            扫码一键建应用(或 DSH_LARK_APP_ID/SECRET 手动通道)
/lark start|stop|restart|status   桥接生命周期与全链路健康
/lark uninstall-clean  清除凭据与状态目录

飞书侧(卡片化单选,无需记忆拼写)

类别命令行为
选择类/mode /permission /model单选按钮卡片,点选即切换(动态感知自建 preset 与提供商)
目标类/goal [目标|pause|resume|clear]启动长任务自主执行 / 暂停 / 恢复 / 清除当前目标
状态类/status /sessions /help全链路健康(含 Outbox/补发计数)/ 会话列表 / 帮助卡片
会话类/new /resume [序号|id] /stop /workspace <路径>新会话 / 极简恢复历史会话 / 停当前任务 / 切工作区
诊断/doctorZIP 诊断包(session log + 配置 + ISSUE.md)
热改/lark-config key=value热改配置(如 groupPolicy=open、agentPreset=standard、streaming.enabled=true)
DSH 命令/compact 等原生执行,结果回飞书
多媒体发图片/文件图片→视觉模型;文件→文本提取
意图确认模型提问自动转飞书意图确认卡片,选项或输入作答

命令无拦截、无门禁:一切 / 消息要么桥处理,要么原样交 DSH——绝不静默丢弃。skill 无前缀,直接说任务即可。

⚙️ 常用配置(/lark-config 热改,立即生效并持久化)

配置键默认说明
groupPolicyopen群聊触发策略:open(免 @ 全触发)/ mention / keywords / reply
groupKeywords["lark","bot"]keywords 模式下的触发词
agentPresetptcAgent preset(shipped:standard/ptc/minimal/cordis,或 GUI 自建 id;历史别名 code 会自动映射为 ptc)
permissionModedanger-full-access权限:read-only / workspace-write / danger-full-access
streaming.enabledfalseCardKit 流式卡片(开=逐字打印)
reactions.enabledtrue表情回执
allowlist[]open_id 白名单,空 = 所有人可对话
denyList[]命令前缀拒绝兜底
workspaceRoot``桥会话工作区根目录(空 = process.cwd())
attachments.retentionHours168入站图片/文件的保留时长(小时,默认 7 天;0 = 永久保留)。默认存系统临时目录,到期自动清扫
attachments.dir``入站媒体根目录覆盖(空 = 系统 tmpdir;重启生效)

凭据(appId/appSecret)存放在 DSH credentials 服务,不进配置文件;/lark setup 扫码自动写入。

🩺 遇到问题?

  1. 飞书发 /doctor,得到 ZIP 诊断包(完整 session log + 脱敏配置 + ISSUE.md 模板)
  2. 把诊断包贴给任意 AI(或在 GitHub Issue 中发出来),即可快速定位
  3. /status 可随时看连接 / Outbox / 补发 / 会话全链路健康

🛠 开发者

npm run dev:link   # 链接本地 DSH checkout(类型检查/测试需要)
npm run check      # tsc --noEmit
npm test           # 264 项单元 + 集成测试
npm run build      # tsdown → dist/(宿主 ESM + client bundle)
npm pack           # 产出可分发 tarball

架构:桥 = Cordis 插件(dsh.bundle 格式),分层清晰:

host(SDK 适配/认证)→ inbound(传输/群触发/断连补偿/Inbound WAL)→ application(命令路由/消息编排/诊断)→ outbound(Outbox/事件转发/卡片)→ sessions(每会话 Agent 管理)。

CI(GitHub Actions):push/PR 自动跑类型检查 + 264 项测试 + 构建;发布 npm 走 tag release 的 Publish workflow。

📄 许可

MIT — 自由使用、修改、分发。

本项目为社区插件,与 DeepSeek、飞书或 Lark 无隶属关系。


English

dsh-lark-plus mascot

DeepSeek Harness × Feishu/Lark bridge — put your DSH agent inside Feishu. Scan a QR code and go live in 30 seconds; chat from anywhere. Plus fully local voice transcription and a play bar in the Web GUI.

🌟 Why dsh-lark-plus

  • Zero setup friction: QR-scan app creation — no Open Platform fiddling, no callback URL, no public server
  • Zero message loss: durable Outbox (outbound) + Inbound WAL — crashes, hot reloads, and dsh restarts all recover
  • Zero learning curve: every toggle is a single-select card; native DSH commands (/goal /compact …) just work
  • A real agent: full tool belt (bash / files / subagents / workflows) driven from Feishu

✨ Features

CapabilityDescription
🎯 One-click auth/lark setup scans a QR to create the Feishu app (auto-subscribes message events + group-all + reactions). 30-second onboarding; manual DSH_LARK_APP_ID/SECRET channel also supported
🧠 Multi-mode AgentStandard / Code / Minimal / Cordis presets + your custom GUI presets; /mode shows a single-select card — tap to switch (default Code: multi-step tools in one shot)
🎛 Permission tiersRead-only / workspace-write / Full access; /permission card switches instantly (Full access by default)
🎨 Card-based commands/mode /permission /model are all single-select button cards — tap, no typing; models grouped by provider
💬 Intent confirmationModel questions (ask_user_question) land as Feishu intent-confirmation cards (option buttons + multi-select dropdown + custom text); answer and the agent resumes
😊 Reaction receiptsRandom "got it" reaction on inbound; DONE ✅ on completion (only Feishu-validated emojis)
💪 Outbound zero-lossPersistent Outbox (JSONL + at-least-once + idempotency + per-lane parallel + failure quarantine + periodic prune), resumes after kill/restart; bridge command replies ride the Outbox too
🆕 Inbound request replayIf the agent dies / plugin reloads / dsh restarts MID-TURN, the interrupted user message is auto re-triggered on boot (no more silently dropped requests) — durable Inbound WAL + boot reconciliation + attempt/time caps; /status shows the pending-replay count
🛡 Self-healing connectionProbe-driven controlled reconnect + QuotaGovernor circuit breaker (auto-unblocks and reconnects after the quota window) + missed-message compensation; auto-avoids proxy env
🔀 3-tier command routingBridge commands → bridge; DSH commands → native; /goal, unknown /xxx, plain text → injected verbatim (no gates). Skills need no prefix — just describe the task
📎 Inbound mediaFeishu images → visual model (attachment-backed); files → bounded text extraction
📤 Outbound mediaModel sends local files/images via lark_plus_send_local_file (workspace whitelist + size/format checks)
🩺 One-click diagnostics/doctor → ZIP bundle (full DSH session log + sanitized config + ISSUE.md) back to the chat
✍️ Markdown renderingReplies auto-render as CardKit cards (headings/lists/code/tables); plain text stays plain
🌊 Optional streaming/lark-config streaming.enabled=true hot-enables CardKit schema 2.0 streaming cards (off by default, saves traffic)
🆕 Goal-driven long tasks/goal <objective> launches autonomous long-running task loops directly from Feishu; /goal pause / resume / clear manage execution via clean natural conversation
🆕 Session management/new opens a fresh session; /resume clean-restores a historical session (button/index/id-prefix, resolves true titles, fixes cross-restart session collision); /workspace <path> switches; per-session isolated configuration
🖥 Reuses DSH Web GUIBridge agents are native DSH sessions; conversations auto-group under their workspace; the web panel shows live Outbox/replay counters
👥 Access controlallowlist restricts inbound to specific open_ids; groupPolicy (open / mention / keywords / reply); denyList command-prefix deny
🔓 Full access by defaultSandbox full access + never-ask approvals

🚀 Quickstart

Prerequisites: Node.js ≥ 24 and DeepSeek Harness installed (npm i -g @deepseek-ai/dsh).

dsh plugin --profile web add dsh-lark-plus@latest --ignore-scripts
dsh web
/lark setup          # scan QR (30s)
/lark start

Open Feishu, find your bot, send anything — reaction receipt + full reply = end-to-end. Group chats need no @-mention.

Install variants: local tarball (npm pack, then dsh plugin --profile web add ./dsh-lark-plus-<version>.tgz --ignore-scripts) or GitHub source (github:amlyczz/dsh-lark-plus, requires build approval). Upgrade with dsh plugin --profile web update dsh-lark-plus --latest --ignore-scripts.

Updating after each release

Don't rely on @latest: it resolves the mirror's dist-tags.latest label — npmmirror-style mirrors can serve a stale tag (version metadata synced, tag still pointing at an older release), so pnpm sees Already up to date even though a newer version exists. The tag is what the mirror says; the version number is what you say.

# 1. See the real version list, bypassing tags:
npm view dsh-lark-plus versions --registry https://registry.npmjs.org

# 2. Install by explicit version (most reliable):
dsh plugin --profile web add dsh-lark-plus@<new-version> --ignore-scripts

# 3. Or force the official registry with @latest:
dsh plugin --profile web add dsh-lark-plus@latest --ignore-scripts --registry https://registry.npmjs.org

Suspicious Already up to date? Run dsh plugin --profile web outdated first — if it also shows the old version, the mirror tag is stale; use an explicit version. Restart dsh web after installing.

⌨️ Commands (Feishu side)

  • Selectors (single-select cards): /mode /permission /model
  • Goals: /goal [objective|pause|resume|clear] (autonomous tasks / pause / resume / clear)
  • Status: /status /sessions /help
  • Sessions: /new /resume [index|id] /stop /workspace <path>
  • Diagnostics: /doctor (ZIP with session log)
  • Hot reload: /lark-config key=value
  • DSH commands run natively: /compact …
  • Media: send images/files to the bot
  • Intent confirmations auto-arrive as cards

⚙️ Configuration (/lark-config, hot-reloaded & persisted)

KeyDefaultMeaning
groupPolicyopengroup trigger: open / mention / keywords / reply
agentPresetptcagent preset (standard/ptc/minimal/cordis or custom; legacy alias code maps to ptc)
permissionModedanger-full-accessread-only / workspace-write / danger-full-access
streaming.enabledfalseCardKit streaming cards
reactions.enabledtruereaction receipts
allowlist[]open_id allowlist (empty = everyone)
denyList[]command-prefix deny
workspaceRoot``workspace root for bridge sessions
attachments.retentionHours168retention (hours) for inbound images/files (default 7 days; 0 = keep forever). Stored under the OS temp dir and swept by age
attachments.dir``inbound media root override (empty = OS tmpdir; applies after reload)

Credentials (appId/appSecret) live in the DSH credentials service, never in config files.

🩺 Troubleshooting

Send /doctor in Feishu — you get a ZIP with the full session log, sanitized config, and an ISSUE.md template. Hand it to any AI (or open a GitHub Issue). /status shows live connection / Outbox / replay health.

🛠 Development

npm run dev:link && npm run check && npm test && npm run build

264 unit + integration tests. GitHub Actions CI runs typecheck + tests + build on every push/PR; npm publishing rides the release-tagged Publish workflow.

Architecture (Cordis plugin, dsh.bundle format): host (SDK adapter/auth) → inbound (transport/group policy/compensation/Inbound WAL) → application (command routing/orchestration/diagnostics) → outbound (Outbox/event forwarding/cards) → sessions (per-chat agent management).

📄 License

MIT — free to use, modify, and distribute.

This is a community plugin, not affiliated with DeepSeek, Feishu, or Lark.


归属与许可

  • 本项目基于 dsh-lark-link(MIT License,Copyright (c) 2026 dsh-lark-link contributors)二次开发,保留了原项目的全部版权与许可声明(见 LICENSE)。
  • 新增部分:本地离线语音转写链路(src/voice/、消息链路的 audio 分支、本机转写路由)以及相应的测试。
  • 语音引擎 SenseVoice 与运行时 sherpa-onnx 由各自作者以 Apache-2.0 许可发布;模型文件不随本仓库分发,首次使用时从官方发布页下载。
  • 本仓库以 MIT License 发布,见 LICENSE。

Plugins associés