Buck Blog · 博客正文

返回技术分享首页
用飞书远程控制本机 Claude Code:从零配置与踩坑全记录

用飞书远程控制本机 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 干活」的环境,并把配置过程中遇到的所有坑(环境变量丢失、会话绑定、按名字恢复、上下文监控、多机扩展等)逐一列出现象 → 根因 → 修复。照着本文,你能复现出同样的一套环境。

整体链路:
graph LR A[手机飞书] -->|发消息 / @机器人| B[飞书机器人
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 起) 较新、有速率/单绑定限制
结论:如果你用的是 Anthropic 官方 API,官方 Remote Control 最省事;如果你用第三方 Anthropic 兼容模型(如 DeepSeek、Kimi、GLM),官方 Remote Control 用不了,飞书 + lark-channel-bridge 是最成熟、无需公网的选择。

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
关键点:桥接恢复会话时,先查 catalog,查不到才回退到 sessions.json(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 是干活的。所有坑几乎都出在「环境变量没传下去」和「会话映射没改对两处」这两件事上。
本文所有项目名、账号、密钥均已脱敏,读者可自行替换为占位符中的真实值。