核心部分代码展示
总览
核心代码展示总览
这部分展示项目里最关键的工程代码,以及这些代码如何从飞书入口、会话绑定、Claude Code 调用、流式卡片、Gateway 自动化一路串成可运行的系统。
链路结构
- CLI onboarding:oh-my-feishu 一条命令启动交互式配置。
- 飞书 WebSocket 负责用户入口,MessageRouter 把输入路由到命令、流程或 Claude 会话。
- Session 绑定让 Claude Code 拿到本地目录和历史会话,实现有上下文的仓库级工作。
- 流式卡片让长时间 AI 执行过程可见,120ms 节流更新确保实时性又不过载。
- Gateway 与 Web Monitor 让 AI 从被动响应扩展为事件驱动自动化。
相关文件
Feishu message / card action
-> FeishuWebSocket (WebSocket 长连接)
-> MessageRouter / CardDispatcher (路由分发)
-> SessionStore + SessionHistoryStore (会话绑定)
-> invokeClaudeChat / invokeClaudeTask (AI 执行)
-> CardKit streaming update (实时卡片)
-> Feishu result card (结果回传)CLI 启动
CLI 启动与交互式配置
代码作用
项目要落地,第一步是让用户能稳定启动服务。CLI 入口同时支持交互式 TUI 和命令模式,把安装检查、飞书注册、服务管理、Web Monitor 管理收敛到统一入口。
拆解要点
- 无子命令时进入 Ink TUI,适合首次配置和交互式引导。
- session、gateway、web-monitor 子命令保留脚本化能力,适合演示和自动化。
- 扫码授权流程使用飞书设备码注册,授权后自动写入 lark-cli 兼容配置。
- PM2 配置支撑后台进程稳定运行和日志持久化。
const args = process.argv.slice(2);
const command = args[0];
if (!command) {
await import('./index.js');
return;
}
if (command === 'session') {
const action = args[1] as SessionCommandOptions['action'];
await handleSessionCommand({ action });
return;
}飞书入口
飞书 WebSocket、消息路由与卡片交互
代码作用
飞书侧入口需要同时处理普通消息、slash command、卡片按钮和交互流程。代码把 WebSocket 接入、命令注册、卡片分发、Gateway runtime 初始化集中在 FeishuWebSocket 中,再把文本消息交给 MessageRouter。
拆解要点
- EventDispatcher 注册 im.message.receive_v1 和 card.action.trigger 事件。
- processedMessageIds 做消息去重,避免飞书重复推送导致重复执行。
- MessageRouter 先处理 active flow,再处理 slash command,最后进入 Claude 对话。
- CommandRegistry 注册 /repair、/status、/service、/help、/menu、/claude 等命令。
- CardDispatcher 统一处理卡片按钮回调,支持确认流程、服务管理等复杂交互。
相关文件
const commandMatch = text.match(/^(\/\S+)(?:\s+(.*))?$/s);
if (commandMatch) {
const command = commandMatch[1];
const args = commandMatch[2]?.trim().split(/\s+/) || [];
await this.handleCommand(command, args, chatId, chatType, senderOpenId, messageId);
return;
}
await this.handleChat(chatId, chatType, text, senderOpenId, messageId);会话绑定
Claude Code 会话创建与目录绑定
代码作用
普通聊天机器人通常只知道聊天文本,不知道本地项目。目录会话让飞书 chatId 绑定到本地目录和 Claude sessionId,Claude Code 可以在真实 cwd 中继续历史工作。
拆解要点
- SessionAddFlow 校验目录路径,列出该目录已有 Claude Code session。
- 用户可以选择已有 session 继续,也可以新建目录会话。
- completeSession 把 directory 和 sessionId 写入当前 chat 的 SessionStore。
- SessionHistoryStore 记录历史绑定,方便后续恢复和查看。
- chatIdToSessionId 生成稳定的 session ID,支持同一聊天持续上下文。
相关文件
const sessions = await listSessions(trimmedDir);
if (sessions.length > 0) {
return {
card: createDirectorySessionSelectCard(trimmedDir, sessions).card,
toast: '请选择要连接的 Claude Code 会话',
};
}
await this.completeSession(chatId, trimmedDir, null);Claude 调用
Claude Code 调用与流式输出
代码作用
Claude Code 是项目的 AI 执行核心。invoker 负责设置 cwd、恢复 session、注入飞书上下文环境变量,并解析 Claude stream-json 输出。
拆解要点
- directory 存在时用用户绑定目录作为 cwd,否则回到 workspace 默认目录。
- 用 chatIdToSessionId 生成稳定 session,支持同一聊天持续上下文。
- 使用 --output-format stream-json、--include-partial-messages 获取增量事件。
- 如果 --resume 找不到历史会话,自动用 --session-id 重试创建新会话。
- buildFeishuContextEnv 注入飞书上下文环境变量,让 Claude 知道发送者和聊天信息。
const cwd = context.directory
? resolve(context.directory)
: resolve(env.REPO_ROOT, 'workspace');
const sessionId = context.sessionId || chatIdToSessionId(context.chatId);
const claudeArgs = [
'-p',
'--dangerously-skip-permissions',
'--output-format', 'stream-json',
'--resume', sessionId,
prompt
];流式卡片
Claude Code 流式输出到飞书卡片
代码作用
AI coding 任务经常耗时较长。如果只等待最终文本,飞书用户会觉得无反馈。流式卡片把等待、思考、工具调用和正文输出持续展示出来。
拆解要点
- 先通过 CardKit API 创建 streaming_mode 卡片,立即发送到飞书。
- 正文和思考分别进入不同 markdown 元素,思考内容放入折叠面板。
- 用 accumulated Map 维护每个元素的完整内容,因为 updateCardContent 发送的是全文。
- 120ms 节流更新,控制飞书卡片更新频率,避免 API 过载。
- CardKitManager 封装 createCard、updateCardContent、updateCardProps 等操作。
const scheduleTextFlush = () => {
if (textFlushTimer) return;
textFlushTimer = setTimeout(async () => {
textFlushTimer = null;
await doFlushText().catch(() => {});
if (pendingTextDeltas.length > 0) scheduleTextFlush();
}, 120);
};Gateway
Gateway feature runner 与事件驱动自动化
代码作用
Gateway 把飞书命令、timer、webhook、internal 事件统一成 GatewayEvent,再由 feature registry 匹配处理器。Web Monitor 基于 traceback hash 变化触发 AI 分析或修复。
拆解要点
- GatewayFeatureRunner 负责 feature 匹配、日志、异常兜底和统一返回结构。
- GatewayRuntime 向 feature 注入 invokeMainClaude、sendFeishuMessage、sendFeishuCard、updateCard 等能力。
- Web Monitor 支持 requireConfirmation,必要时先发确认卡片,保留 human-in-the-loop。
- TracebackMonitor 支持 json/text/html traceback 提取,并用 SHA-256 hash 去重。
- 自动 PR 配置支持设置目标分支、Draft 模式和分支前缀。
相关文件
async run(event: GatewayEvent): Promise<GatewayResult> {
const feature = this.options.registry.match(event);
if (!feature) {
return { success: false, message: `No Gateway feature matched event "${event.type}"` };
}
return await feature.handle(event, this.options.runtime);
}Web Monitor
Traceback Monitor 日志监控与 AI 触发
代码作用
Web Monitor 是自动化的核心组件。它定时轮询服务的日志端点,检测新的 traceback,并触发 Claude Code 进行分析或修复。
拆解要点
- 支持 json、text、html 三种日志格式,自动提取 traceback 内容。
- SHA-256 hash 去重,只有新的 traceback 才会触发 AI。
- 可配置轮询间隔,默认 60 秒检查一次。
- 触发时通过 Gateway event 分发,复用 feature runner 的能力注入。
- 支持 requireConfirmation 模式,先发确认卡片让用户决定是否执行。
相关文件
function extractLatestTraceback(content, urlType) {
if (urlType === 'json') {
const parsed = JSON.parse(content);
const entries = Array.isArray(parsed) ? parsed : parsed.logs ?? [parsed];
for (let i = entries.length - 1; i >= 0; i--) {
const tracebackText = entries[i].traceback ?? entries[i].error ?? entries[i].message;
if (tracebackText) return { traceback: tracebackText, hash: hashTracebackContent(tracebackText) };
}
}
return null;
}Skills
Claude plugin、skills 与飞书能力扩展
代码作用
Claude Code 擅长代码仓库任务,但不知道飞书 API 的调用规则。项目用 plugin 和 skills 把飞书业务域、lark-cli 命令、安全约束和 Gateway 协议交给 Claude。
拆解要点
- marketplace.json 声明本仓库作为 Claude plugin marketplace,支持 claude plugin add。
- lark-chat-guide 规定普通聊天直接输出,飞书操作走 lark-cli 命令行。
- gateway-guide 让 Claude 能通过 oh-my-feishu gateway 触发后台 feature。
- Web Monitor skills 定义自动分析、修复、通知、服务管理和安全检查协议。
- 技能文件使用 Markdown 格式,易于维护和扩展。
相关文件
## 核心规则
当用户只是普通聊天,直接将回复内容输出到 stdout。
当用户请求涉及飞书平台操作,使用对应的 lark 技能通过 lark-cli 执行操作。
## 安全约束
- 永远不要在回复中暴露 app_id、app_secret 或用户敏感信息
- 群操作前检查 bot 是否在群内
- 文档操作前检查是否有读写权限