Bridge service connecting Feishu (Lark) to dfcode REST API
Project description
DfCode Remote — Feishu Bridge
Python 服务,通过飞书 WebSocket 长连接接收用户消息,调用 dfcode 的 REST API 控制编码会话,通过 SSE 事件流获取实时更新,渲染为飞书卡片推送给用户。
概述
核心优势:dfcode 已有完整的 HTTP API + SSE 事件流 + 结构化消息数据,不需要像 remote_claude 那样用 PTY + pyte 解析终端输出。我们直接拿到 markdown、tool calls、permissions 等结构化数据。
| 维度 | remote_claude | dfcode remote |
|---|---|---|
| 数据源 | PTY + pyte 解析终端输出 | REST API + SSE 结构化数据 |
| 通信协议 | Unix Socket + mmap 共享内存 | HTTP + SSE |
| 输出解析 | ANSI 转义序列 → 组件 | MessageV2 Parts(已结构化) |
| 进程管理 | pty.fork() 启动 CLI | dfcode serve 独立运行 |
| 卡片内容 | 终端文本 + font color | Markdown + 工具调用摘要 |
架构图
飞书用户 ──→ 飞书服务器 ──→ WebSocket 长连接 ──→ packages/remote (Python)
│
┌────┴────┐
│ │
HTTP API SSE Stream
(控制) (监听)
│ │
└────┬────┘
│
dfcode serve :4096
(headless 模式)
数据流
- 用户在飞书发消息 → WebSocket 事件 →
handler.py路由 - 命令消息 → 直接处理(/attach, /list, /kill 等)
- 普通消息 →
dfcode_client调用POST /session/:id/prompt_async - SSE 事件流
GET /event→event_listener.py实时监听 message.part.updated事件 → 增量更新飞书卡片permission.asked事件 → 渲染交互按钮卡片question.asked事件 → 渲染选项按钮卡片session.idle事件 → 标记完成状态
快速开始
前置条件
- Python >= 3.10
- 已创建的飞书应用(自定义应用)
- 运行中的 dfcode serve(默认端口 4096)
1. 安装
pip install dfcode-remote
从源码安装(开发模式):
cd packages/remote
pip install -e .
2. 飞书应用配置
- 登录飞书开放平台
- 创建自定义应用,获取 App ID 和 App Secret
- 在「权限管理」中添加以下权限:
im:message— 接收消息im:message:send_as_bot— 以机器人身份发送消息im:chat— 获取群组信息cardkit:card— 使用 CardKit 发送/更新卡片
- 在「事件订阅」中启用以下事件(均使用长连接模式):
im.message.receive_v1— 接收消息事件card.action.trigger— 卡片按钮点击回调(在「卡片回调」中单独配置)
- 在「版本管理与发布」中发布应用
- 将机器人添加到飞书群组
3. 配置环境变量
通过 .env 文件或直接设置环境变量:
export FEISHU_APP_ID=cli_xxxxxxxxxxxx
export FEISHU_APP_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
export DFCODE_URL=http://localhost:4096
或复制模板:
cp .env.example .env
# 编辑 .env 填入你的配置
4. 启动
# 先启动 dfcode 服务(另一个终端)
dfcode serve
# 或开发模式:bun dev serve
# 启动飞书 bridge
dfcode-remote
# 指定 dfcode 地址
dfcode-remote --dfcode-url http://host:4096
# 启用调试日志
dfcode-remote --debug
# 也可以用 python -m 方式运行
python -m dfcode_remote
环境变量
| 变量名 | 说明 | 默认值 | 必填 |
|---|---|---|---|
FEISHU_APP_ID |
飞书应用 App ID | - | 是 |
FEISHU_APP_SECRET |
飞书应用 App Secret | - | 是 |
DFCODE_URL |
dfcode serve 地址 | http://localhost:4096 |
否 |
DFCODE_DIRECTORY |
默认工作目录 | . |
否 |
DFCODE_USERNAME |
dfcode 基础认证用户名 | - | 否 |
DFCODE_PASSWORD |
dfcode 基础认证密码 | - | 否 |
ALLOWED_USERS |
允许的用户 ID 列表(逗号分隔) | - | 否 |
ENABLE_URGENT |
任务完成时发送加急通知 | true |
否 |
支持的命令
| 命令 | 用法 | 说明 |
| --------- | -------------------- | ------------------------------------ | ------------------ |
| /menu | /menu | 显示命令帮助菜单 |
| /list | /list | 列出跨项目“可继续对话”的活跃会话 |
| /new | /new [title] | 创建新会话并绑定到当前群 |
| /attach | /attach <序号 | session_id> | 绑定到已有活跃会话 |
| /detach | /detach | 解绑当前会话 |
| /status | /status | 查看当前绑定会话的状态 |
| /kill | /kill [session_id] | 终止会话(不指定则终止当前绑定会话) |
使用示例
/new 帮我写个爬虫
→ ✅ 已创建并绑定会话
id: ses_xxx
dir: /path/to/project
写个 Python 脚本抓取 example.com 的标题
→ [卡片流式更新中...]
/list
→ 1. 项目A
id: ses_xxx
dir: /Users/foo/project-a
status: busy
/attach 1
→ ✅ 已绑定会话
id: ses_xxx
dir: /Users/foo/project-a
/status
→ id: ses_xxx
title: 项目A
dir: /Users/foo/project-a
status: busy
/detach
→ ✅ 已解绑当前会话
/attach ses_xxx
→ ✅ 已绑定会话 ses_xxx
/kill
→ ✅ 已终止会话 abc12345
卡片交互
权限请求
当 dfcode 需要执行敏感操作(如写入文件)时,卡片会显示交互按钮:
🔐 write_file: /path/to/file
[允许] [始终允许] [拒绝]
- 允许:仅本次允许该操作
- 始终允许:永久允许此类操作
- 拒绝:拒绝该操作
点击后按钮会变为灰色(已处理状态),dfcode 继续执行。
问题选择
当 dfcode 需要用户选择时,卡片会显示选项按钮:
❓ 请选择要使用的框架:
[React] [Vue] [Angular]
点击选项后,选择会发送给 dfcode。
中止按钮
当会话处于 busy 状态时,卡片右下角显示中止按钮:
⛔ 中止
点击后会调用 POST /session/:id/abort 终止当前任务。
事件流
event_listener.py 监听以下 SSE 事件并映射到卡片更新:
| SSE 事件类型 | 处理逻辑 | 卡片更新 |
|---|---|---|
server.connected |
忽略 | - |
server.heartbeat |
忽略 | - |
message.part.updated (type=text) |
追加/更新文本内容 | 内容区 markdown 更新 |
message.part.updated (type=tool, status=running) |
添加工具执行中面板 | 折叠面板(⏳ 图标) |
message.part.updated (type=tool, status=completed) |
更新工具完成状态 | 折叠面板(🔧 图标)+ 输出摘要 |
message.part.updated (type=step-finish) |
更新 token 用量 | 状态区显示 tokens/cost |
message.part.updated (type=reasoning) |
添加推理内容 | 可折叠面板(💭 Reasoning) |
permission.asked |
渲染权限按钮 | 交互区显示允许/拒绝按钮 |
question.asked |
渲染问题选项 | 交互区显示选项按钮 |
session.status (idle) |
标记完成 | 状态变为 ✅ 完成,发送通知 |
session.error |
显示错误 | 发送错误文本消息 |
防抖机制
- 0.5 秒内多次事件合并为一次
update_card调用 - 减少飞书 API 调用频率
卡片分裂
飞书卡片有内容块数量限制(MAX_CARD_BLOCKS = 50)。当内容块接近限制时:
- 当前卡片被冻结(
build_frozen_card)— 移除交互区,显示完成状态 - 创建新卡片继续接收后续内容
- 旧卡片保留历史内容,新卡片显示新内容
触发条件:
if state.content_block_count() >= MAX_CARD_BLOCKS - 5:
await self._freeze_and_split(state)
测试
# 运行所有测试
python -m unittest discover -s tests -v
# 运行特定测试文件
python -m unittest tests/test_card_builder.py -v
python -m unittest tests/test_sse.py -v
测试覆盖
test_card_builder.py— 卡片构建、流式卡片、冻结卡片、菜单卡片test_markdown.py— Markdown 转换、工具输出格式化、截断逻辑test_persistence.py— 绑定持久化、原子写入test_types.py— 数据类解析(SessionInfo, PartInfo, SSEEvent 等)test_sse.py— SSE 流解析、帧解析、超时处理
验证流程
冒烟测试
dfcode serve启动 dfcode(或bun dev serve)dfcode-remote启动飞书 bridge- 飞书群发
/new→ 创建会话 - 发送 "帮我在当前目录创建一个 hello.py 文件"
- 观察卡片流式更新 → 权限弹窗 → 点击允许 → 完成通知
/list确认会话状态/kill终止会话
项目结构
packages/remote/
├── pyproject.toml # pip 包配置
├── .env.example # 环境变量模板
├── README.md # 本文档
├── main.py # 兼容入口(委托给 dfcode_remote.cli)
├── dfcode_remote/
│ ├── __init__.py # 包版本
│ ├── __main__.py # python -m dfcode_remote 入口
│ ├── cli.py # CLI 入口:解析参数、启动 bot + SSE
│ ├── dfcode_client/
│ │ ├── client.py # httpx 异步客户端,封装 dfcode REST API
│ │ ├── sse.py # SSE 事件流消费者
│ │ └── types.py # 响应类型定义(dataclass)
│ ├── lark_client/
│ │ ├── bot.py # 飞书 WebSocket 长连接启动
│ │ ├── handler.py # 命令路由 + 消息处理
│ │ ├── card_builder.py # dfcode 消息 → 飞书卡片 JSON
│ │ ├── card_service.py # 飞书 CardKit API 封装
│ │ └── event_listener.py # SSE → 卡片更新桥接
│ └── utils/
│ ├── config.py # dotenv 配置加载
│ ├── markdown.py # Markdown → 飞书卡片内容转换
│ └── persistence.py # JSON 文件持久化(chat 绑定)
└── tests/ # 单元测试
已知限制
- 单会话绑定:一个飞书群同一时间只能绑定一个 dfcode 会话(可通过
/attach切换) - 无多群广播:一个 dfcode 会话不能同时推送到多个飞书群
- 无用户白名单 UI:
ALLOWED_USERS需要手动配置用户 ID - 卡片块限制:单卡片最多 50 个内容块,超出会触发卡片分裂
- 图片不支持:Markdown 中的图片会被替换为
[alt text]占位符 - 长文本截断:单卡片内容超过 30000 字符会被截断
未来工作
-
/start <directory>命令 — 创建指定目录的会话 -
/new-group <name>命令 — 创建飞书群并绑定新会话 - 多会话同时绑定 — 一个群绑定多个会话,支持切换
- 会话完成时 @mention 群成员
- 更细粒度的权限控制(按工具类型)
- 图片上传支持(通过飞书图片 API)
许可证
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dfcode_remote-0.2.8.tar.gz.
File metadata
- Download URL: dfcode_remote-0.2.8.tar.gz
- Upload date:
- Size: 44.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fb3ace7cc882dd7715de7289d6ea947bb70398b3aef71c555e99c456c2c6fff6
|
|
| MD5 |
a36f4c7f7017c14e0458a613eeed4bfe
|
|
| BLAKE2b-256 |
7443dd4da1a09a40e159b5c5a3ea609f7e3fa92478a3bd88e4d0e721c784ed1c
|
File details
Details for the file dfcode_remote-0.2.8-py3-none-any.whl.
File metadata
- Download URL: dfcode_remote-0.2.8-py3-none-any.whl
- Upload date:
- Size: 32.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
92ed9c430f8685072f7a90cda1e0d32c831467cdca365c2b6a2fef99388d0b74
|
|
| MD5 |
f1d3684b4b220fb6e0594783fe8e764d
|
|
| BLAKE2b-256 |
59b1acf981d679544a3bdb1dee5370a32593c64c03038e6b9959d2d8859756d3
|