Skip to main content

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 模式)

数据流

  1. 用户在飞书发消息 → WebSocket 事件 → handler.py 路由
  2. 命令消息 → 直接处理(/attach, /list, /kill 等)
  3. 普通消息 → dfcode_client 调用 POST /session/:id/prompt_async
  4. SSE 事件流 GET /eventevent_listener.py 实时监听
  5. message.part.updated 事件 → 增量更新飞书卡片
  6. permission.asked 事件 → 渲染交互按钮卡片
  7. question.asked 事件 → 渲染选项按钮卡片
  8. session.idle 事件 → 标记完成状态

快速开始

前置条件

  • Python >= 3.10
  • 已创建的飞书应用(自定义应用)
  • 运行中的 dfcode serve(默认端口 4096)

1. 安装

pip install dfcode-remote

从源码安装(开发模式):

cd packages/remote
pip install -e .

2. 飞书应用配置

  1. 登录飞书开放平台
  2. 创建自定义应用,获取 App IDApp Secret
  3. 在「权限管理」中添加以下权限:
    • im:message — 接收消息
    • im:message:send_as_bot — 以机器人身份发送消息
    • im:chat — 获取群组信息
    • cardkit:card — 使用 CardKit 发送/更新卡片
  4. 在「事件订阅」中启用以下事件(均使用长连接模式):
    • im.message.receive_v1 — 接收消息事件
    • card.action.trigger — 卡片按钮点击回调(在「卡片回调」中单独配置)
  5. 在「版本管理与发布」中发布应用
  6. 将机器人添加到飞书群组

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)。当内容块接近限制时:

  1. 当前卡片被冻结build_frozen_card)— 移除交互区,显示完成状态
  2. 创建新卡片继续接收后续内容
  3. 旧卡片保留历史内容,新卡片显示新内容

触发条件:

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 流解析、帧解析、超时处理

验证流程

冒烟测试

  1. dfcode serve 启动 dfcode(或 bun dev serve
  2. dfcode-remote 启动飞书 bridge
  3. 飞书群发 /new → 创建会话
  4. 发送 "帮我在当前目录创建一个 hello.py 文件"
  5. 观察卡片流式更新 → 权限弹窗 → 点击允许 → 完成通知
  6. /list 确认会话状态
  7. /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/                      # 单元测试

已知限制

  1. 单会话绑定:一个飞书群同一时间只能绑定一个 dfcode 会话(可通过 /attach 切换)
  2. 无多群广播:一个 dfcode 会话不能同时推送到多个飞书群
  3. 无用户白名单 UIALLOWED_USERS 需要手动配置用户 ID
  4. 卡片块限制:单卡片最多 50 个内容块,超出会触发卡片分裂
  5. 图片不支持:Markdown 中的图片会被替换为 [alt text] 占位符
  6. 长文本截断:单卡片内容超过 30000 字符会被截断

未来工作

  • /start <directory> 命令 — 创建指定目录的会话
  • /new-group <name> 命令 — 创建飞书群并绑定新会话
  • 多会话同时绑定 — 一个群绑定多个会话,支持切换
  • 会话完成时 @mention 群成员
  • 更细粒度的权限控制(按工具类型)
  • 图片上传支持(通过飞书图片 API)

许可证

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dfcode_remote-0.2.8.tar.gz (44.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dfcode_remote-0.2.8-py3-none-any.whl (32.9 kB view details)

Uploaded Python 3

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

Hashes for dfcode_remote-0.2.8.tar.gz
Algorithm Hash digest
SHA256 fb3ace7cc882dd7715de7289d6ea947bb70398b3aef71c555e99c456c2c6fff6
MD5 a36f4c7f7017c14e0458a613eeed4bfe
BLAKE2b-256 7443dd4da1a09a40e159b5c5a3ea609f7e3fa92478a3bd88e4d0e721c784ed1c

See more details on using hashes here.

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

Hashes for dfcode_remote-0.2.8-py3-none-any.whl
Algorithm Hash digest
SHA256 92ed9c430f8685072f7a90cda1e0d32c831467cdca365c2b6a2fef99388d0b74
MD5 f1d3684b4b220fb6e0594783fe8e764d
BLAKE2b-256 59b1acf981d679544a3bdb1dee5370a32593c64c03038e6b9959d2d8859756d3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page