用飞书远程控制本机 Claude Code:从零配置与踩坑全记录
> 适用环境: Ubuntu/Debian(开发机)+ 手机飞书,Claude Code CLI 2.1.x,lark-channel-bridge 0.7.x
> 最后更新: 2026-08-24
> 核心方案: 飞书机器人 + lark-channel-bridge 桥接进程 + 本机 Claude Code CLI
引言
本文记录如何从零搭建一套「手机飞书远程控制本机 Claude Code 干活」的环境,并把配置过程中遇到的所有坑(环境变量丢失、会话绑定、按名字恢复、上下文监控、多机扩展等)逐一列出现象 → 根因 → 修复。照着本文,你能复现出同样的一套环境。
整体链路:PersonalAgent 应用] B -->|WebSocket 长连接| C[lark-channel-bridge
本机常驻进程] C -->|spawn 子进程| D[本机 Claude Code CLI] D -->|结果回流| C --> B --> A
核心要点:代码执行和文件都在你本机,飞书只是「遥控器」。
1. 方案选型
在动手之前,先想清楚为什么用飞书。市面上能「远程控制本机 Claude Code」的方案大致分三类:
| 方案 | 特点 | 关键限制 |
|---|---|---|
| 飞书 + lark-channel-bridge(本文) | 飞书原生、WebSocket 长连接、无需公网 IP | 每台电脑需独立一个飞书应用 |
| Claude Code 官方 Remote Control | 官方、扫码即用、执行在本机 | 不支持 ANTHROPIC_BASE_URL 指向非官方端点(如 DeepSeek 代理模型不可用) |
| Telegram 桥(tgcc/claude-tg/ctb 等) | Bot API 开放、工具多 | 国内需代理 |
| 微信 iLink / 企业微信桥 | 微信官方 Bot 接口(2026 起) | 较新、有速率/单绑定限制 |
2. 前置环境
2.1 Node.js
lark-channel-bridge 是 Node 包,需 Node 18+(建议 LTS)。
node -v # 确认版本
npm -v
2.2 Claude Code CLI
npm install -g @anthropic-ai/claude-code
claude --version
2.3 模型后端(以 DeepSeek 等 Anthropic 兼容端点为例)
Claude Code 通过三个环境变量指向你的模型后端,写进 ~/.bashrc:
export ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic # 换成你的端点
export ANTHROPIC_AUTH_TOKEN=<你的 AUTH_TOKEN> # 换成你的 token
export ANTHROPIC_MODEL=<你的模型名> # 如 deepseek-v4-pro
export CLAUDE_CODE_SUBAGENT_MODEL=<你的模型名> # 子任务用同模型
export CLAUDE_CODE_MAX_CONTEXT_TOKENS=1000000 # 上下文窗口上限
> 说明:这三行是本机 claude 命令能跑通的前提。后面会反复用到它们——尤其在第 4 节的坑①里。
3. 安装与首次绑定
3.1 安装桥接工具
npm install -g lark-channel-bridge
lark-channel-bridge --version # 本文基于 0.7.x
> 注意:命令名随版本变化,0.7.x 里 run 是前台启动,start/stop/status 是常驻服务管理。
3.2 首次绑定飞书应用
lark-channel-bridge run
首次运行会弹出一个二维码,用手机飞书扫码,即可自动创建并绑定一个飞书「PersonalAgent」应用(也就是你的机器人)。绑定成功后,终端会显示「已连接」,手机飞书里就能和这个机器人私聊了。
发一条消息测试,机器人能回、且能执行命令,就说明链路通了。
> 如果已经有现成的飞书应用,也可以跳过扫码:
>
> lark-channel-bridge run --app-id <你的 app-id>
>
> 它会提示输入 App Secret。
4. 常驻后台 + 坑①:systemd 服务丢失环境变量
前台 run 只适合调试。确认能收发消息后,用服务常驻后台:
lark-channel-bridge start # 装成 systemd 用户服务并启动
lark-channel-bridge status # 查看状态
lark-channel-bridge restart # 重启
lark-channel-bridge stop # 停止并禁用自启
坑①:后台启动后 claude 子进程报 Not logged in
现象:前台 run 一切正常,但 start 变成 systemd 服务后,飞书发消息,机器人回「Not logged in · Please run /login」,或直接没反应。
根因:桥接进程 spawn 出 claude 子进程时,继承的是桥接进程自己的环境变量(源码里 mergeProcessEnv(process.env, …))。而 systemd 服务文件只带了 PATH 和 LARK_CHANNEL_HOME,没有 ~/.bashrc 里的 ANTHROPIC_BASE_URL/ANTHROPIC_AUTH_TOKEN/ANTHROPIC_MODEL。于是子进程找不到登录凭据,退化成「未登录」。
修复:给 systemd 服务补上环境变量,且要用 drop-in(覆盖文件),这样以后 start 重写主 unit 时不会丢:
mkdir -p ~/.config/systemd/user/lark-channel-bridge.bot.claude.service.d
cat > ~/.config/systemd/user/lark-channel-bridge.bot.claude.service.d/env.conf <<'EOF'
[Service]
Environment="ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic"
Environment="ANTHROPIC_AUTH_TOKEN=<你的 AUTH_TOKEN>"
Environment="ANTHROPIC_MODEL=<你的模型名>"
Environment="CLAUDE_CODE_SUBAGENT_MODEL=<你的模型名>"
Environment="CLAUDE_CODE_MAX_CONTEXT_TOKENS=1000000"
EOF
systemctl --user daemon-reload
systemctl --user restart lark-channel-bridge.bot.claude.service
> 验证:systemctl --user status lark-channel-bridge.bot.claude.service 看到 Drop-In: …/env.conf 即为生效。日志在 ~/.lark-channel/profiles/<profile>/logs/daemon/daemon-stdout.log。
5. 核心配置
桥接的配置在 ~/.lark-channel/config.json,关键字段:
| 字段 | 作用 |
|---|---|
profiles.<profile>.workspaces.default |
桥接拉起 claude 时的工作目录(默认一个独立目录,可改成你的项目根目录) |
profiles.<profile>.permissions.defaultAccess |
权限模式(full = 完整权限) |
profiles.<profile>.access.requireMentionInGroup |
群里是否需要 @机器人(默认 true) |
profiles.<profile>.accounts.app |
飞书应用 id / secret |
workspaces.default 改成你常驻的开发目录(如 ~/work),这样飞书机器人操作的文件和你终端 claude 在同一个目录,会话、记忆互通。
> 改完工作目录要同步清空旧会话映射(见第 6 节),否则旧聊天还绑着旧目录。
6. 会话机制
6.1 会话按「聊天」隔离
桥接按 scope(聊天/群/话题/文档评论)隔离会话,互不串:
- 一个私聊 = 一个会话
- 一个群 = 一个会话
- 一个话题(话题群)= 一个会话
不同 scope 可以并行跑(全局并发池默认 10,可调高到 50)。
6.2 会话映射存在哪
~/.lark-channel/profiles/<profile>/sessions.json——chatId → { sessionId, cwd }~/.lark-channel/profiles/<profile>/sessions.json.catalog.json—— agent-aware 索引(含policyFingerprint等)- 真正的对话内容(transcript)在
~/.claude/projects/<cwd路径斜杠换短横>/<sessionId>.jsonl
resumeFor 要求 cwd 完全一致)。
6.3 常用斜杠命令
| 命令 | 作用 |
|---|---|
/new /reset |
清空当前会话 |
/cd <path> |
切换工作目录并重置会话 |
/ws save <name> /ws use <name> |
命名工作空间 |
/resume |
恢复历史会话(只在私聊可用) |
/status |
查看 profile/工作目录/会话状态 |
/help |
帮助卡片 |
/reconnect |
强制 WebSocket 重连 |
> 群里默认要先 @机器人 才会回(私聊不用)。/resume 的会话选择器只在私聊展示,群聊里发会提示「请私聊 bot 使用 /resume」。
7. 按名字绑定与恢复会话
7.1 为什么 --resume 认 ID 不认名字
Claude Code 会话的唯一标识是 UUID(<session-id> 这种),而「会话名」只是你用 /rename 起的显示标签(存在 transcript 的 custom-title 字段),不是唯一键——两个会话可以同名。所以 claude --resume 按 UUID 精确匹配,不能用名字。
7.2 名字存在哪:custom-title 而非 ai-title
ai-title:claude 自动生成的标题custom-title:你/rename起的名字(这才是你想要的「会话名」)
在本机 claude 里 /rename <名字>,名字就存成 custom-title,飞书里和终端列表里都会显示这个名。
7.3 手动把某个群绑到指定会话
坑②:只改 sessions.json 不生效(catalog 优先)要把「群 A」绑到「某个历史会话」,必须同时改两个文件:
sessions.json:"<群chatId>": { "sessionId": "<目标sessionId>", "cwd": "<工作目录>", "updatedAt": <毫秒时间戳> }sessions.json.catalog.json:把该群对应的条目里的sessionId也改成目标值
只改一个会不生效——因为 catalog 是优先查的,catalog 里还是旧 sessionId,sessions.json 的更新根本轮不到。
改完后 systemctl --user restart lark-channel-bridge.bot.claude.service 让桥接重新 load()。
7.4 坑③:restart 会杀掉调用它的 bot
现象:让飞书机器人自己执行「绑定」脚本时,脚本最后一步systemctl restart 会把机器人自己一起杀掉(机器人是桥接的子进程,restart 会连带杀整个 cgroup),导致命令「已被中断」、没回成功消息。
修复:重启动作要延迟 + 解耦,让机器人先回报成功、几秒后再由 systemd 守护进程执行重启:
systemd-run --user --on-active=12 --collect \
--unit=<临时单元名> \
systemctl --user restart lark-channel-bridge.bot.claude.service
7.5 配套脚本与 skill
为了「按名字操作会话、不记 UUID」,可以准备这几个脚本(放 ~/bin/,基于名字匹配 custom-title/ai-title):
| 脚本 | 作用 |
|---|---|
lark-sessions |
列出所有会话(名字 + sessionId) |
lark-resolve <名字> |
名字 → sessionId + 恢复命令 |
lark-context [名字] |
查看上下文占用(见第 8 节) |
lark-bind <名字> |
把当前群绑定到该会话 |
以及一个 skill(b-resume),在本机 claude 里输入 /b-resume <名字>,它会解析出 sessionId 并给出 claude --resume <sessionId> 命令。
> 名字匹配优先级:精确 /rename 名 > 精确自动标题 > 名字子串 > 标题子串。
8. 上下文用量监控
/context 是 Claude Code 交互终端的命令,在桥接的 headless 模式下不生效。要看上下文占用,得从 transcript 里取:
上下文占用 = 最新一次模型调用的 input_tokens + cache_read_input_tokens
(存在 <sessionId>.jsonl 里 assistant 消息的 usage 字段),除以 CLAUDE_CODE_MAX_CONTEXT_TOKENS 得到比例。
lark-context 脚本做了这件事,并支持 --warn 阈值告警:
lark-context # 当前会话的上下文占用
lark-context <名字> # 指定会话
lark-context --warn # 超阈值输出警告,否则 OK(阈值默认 75%)
在桥接的 CLAUDE.md 里加一条指令,让机器人每次回复前先跑 lark-context --warn,超过阈值就把警告放到回复开头,实现「主动提示上下文快满」。
9. 多机扩展:一手机控多台电脑
结论:能,但每台电脑要一个独立的飞书应用(机器人)。飞书 WebSocket 长连接是一个应用一个连接:第二台电脑的桥接如果去连同一个应用,会互相踢下线。所以:
- 第二台电脑装 node + lark-channel-bridge
lark-channel-bridge run扫码绑定一个全新的飞书应用(机器人名起得不一样,如「助手2」)- 配好
ANTHROPIC_*drop-in(第 4 节的坑①)+start常驻
手机飞书里就有了两个机器人,跟哪个聊就控制哪台电脑,完全独立、并行、互不干扰。
10. 坑汇总表
| # | 现象 | 根因 | 修复 |
|---|---|---|---|
| ① | 后台启动后 claude 报 Not logged in |
systemd 服务不带 ANTHROPIC_*,子进程继承不到 |
drop-in env.conf 补环境变量 |
| ② | 改了 sessions.json 绑定不生效 | catalog 优先,只改一处没用 | 同时改 sessions.json + catalog |
| ③ | 绑定脚本执行时被「中断」 | restart 杀掉了调用它的 bot | systemd-run --on-active 延迟解耦重启 |
| ④ | /resume 在群里没反应 |
会话选择器只对私聊开放 | 私聊里用 /resume |
| ⑤ | 会话名在 --resume 里找不到 |
--resume 认 UUID 不认名字 |
用 lark-resolve/b-resume 转成 ID |
| ⑥ | 两个会话名字撞了 | 名字非唯一键,/rename 同名会被自动加后缀 |
起唯一名 |
| ⑦ | 手机飞书突然不回消息 | WebSocket 长连接半开/掉线 | 终端 restart 或 /reconnect 重连 |
11. 附录
11.1 命令速查
# 桥接
lark-channel-bridge run|start|stop|status|restart
# 会话
lark-sessions | lark-resolve <名字> | lark-context [名字] | lark-bind <名字>
# 本机恢复会话
claude --resume <sessionId>
11.2 关键文件路径
| 路径 | 作用 |
|---|---|
~/.lark-channel/config.json |
桥接主配置 |
~/.lark-channel/profiles/<profile>/sessions.json |
会话映射 |
~/.lark-channel/profiles/<profile>/sessions.json.catalog.json |
会话索引 |
~/.lark-channel/profiles/<profile>/logs/ |
桥接日志 |
~/.config/systemd/user/lark-channel-bridge.bot.<profile>.service.d/env.conf |
环境变量 drop-in |
~/.claude/projects/<cwd短横路径>/<sessionId>.jsonl |
会话 transcript |
11.3 一句话总结
飞书是遥控器,桥接是翻译官,claude 是干活的。所有坑几乎都出在「环境变量没传下去」和「会话映射没改对两处」这两件事上。本文所有项目名、账号、密钥均已脱敏,读者可自行替换为占位符中的真实值。