Standalone service that controls multiple ACP backends from a Feishu bot. The bridge runs as an ACP Client and can route each session slot to
cursor-official,cursor-legacy,cursor-tmux,claude, orcodex.
- Forward Feishu messages to Cursor (default path: official
agent acp) - Stream replies to Feishu (interactive cards: answer, thinking, tools, plan, etc.)
- Per-user session isolation (DM / group chat maps to ACP
sessionIdper user) - Multiple sessions: up to 5 concurrent sessions per user per chat, each with its own context and workspace;
/switchbetween them; inactive sessions keep their ACP connection - Max live sessions per user: default 10 across all DMs / groups / threads (tune with
BRIDGE_MAX_SESSIONS_PER_USER;0means unlimited), reducing idle ACP connection buildup when idle timeout is infinite - Group chats: @ the bot (or no @ when “only one human + the bot”); DMs: talk directly
- Explicit sessions: set
BRIDGE_WORK_ALLOWLIST(compatible withCURSOR_WORK_ALLOWLIST); create a session with/new listthen/new <index or path>before normal chat; bare/newlists presets - Built-in commands:
/new,/sessions,/switch,/close(incl./close all),/rename(incl./new list,/new <index>,/new <path>),/status,/mode,/model;/topic+ text is display-only (not sent to the Agent — seedocs/feishu-commands.md) - Persistent Feishu ↔ ACP mapping: after restart, if the Agent reports
loadSession,session/loadcan recover - Recovery metadata:
/statusshows Cursor legacy CLI resume ID or Claude resume session id when the backend exposes one
Feishu user ──(WebSocket)──> FeishuBot ──> Bridge
│
ConversationService / FeishuCardState
│
@agentclientprotocol/sdk ClientSideConnection
│
stdio NDJSON ──> agent acp / cursor-agent-acp / tmux ACP server / claude-agent-acp / codex-acp
- Feishu layer:
src/feishu-bot.ts(SDK + message I/O only) - ACP runtime:
src/acp/runtime.ts+src/acp/feishu-bridge-client.ts(Client: permissions, sandbox read/write, normalizedsession/update) - Orchestration:
src/bridge.ts,src/conversation-service.ts - Sessions:
src/session-manager.ts+src/session-store.ts
- Node.js 18+
- Install the runtime(s) you plan to use: Cursor official uses
agent;cursor-legacystill shells out tocursor-agent;claudeusesclaude-agent-acp/ Claude Code authentication;codexuses@zed-industries/codex-acp - Feishu enterprise app: bot,
im:message,im:message.group_msg(required for group messages),im:message:send_as_bot,im:chat; for “one user + bot” no-@ logic, grant read chat / member APIs as needed (im:chatrelated)
npm install
cp .env.example .env
# Edit .env
npm run dev
# (`tsx src/index.ts`: `cursor-legacy` still defaults to cursor-agent-acp sources via tsx; other backends use their configured spawn command)
# or
npm run build && npm start
# Debug: stop other instances before dev (single-instance lock)
# ./scripts/bridge-dev.sh
# npm run dev:restartbash service.sh install / update always starts the same bridge process; which backend actually runs is decided by ACP_BACKEND / /new --backend ... and the commands available on the host.
| Backend | Required local command | Extra prerequisite | Key env vars | Recommended smoke / check |
|---|---|---|---|---|
cursor-official |
agent |
Run agent login on the host if needed |
ACP_BACKEND=cursor-official, optional CURSOR_AGENT_PATH, CURSOR_API_KEY, CURSOR_AUTH_TOKEN |
Confirm agent acp works locally, then use /new --backend cursor-official ... or normal /new |
cursor-legacy |
Node.js + in-repo cursor-agent-acp/ |
Local cursor-agent / Cursor CLI must still be usable |
ACP_BACKEND=cursor-legacy, optional CURSOR_LEGACY_NODE_PATH, CURSOR_LEGACY_SESSION_DIR, CURSOR_LEGACY_EXTRA_ARGS |
Confirm npm run dev / npm run build succeeds, then use /new --backend cursor-legacy ... |
cursor-tmux |
tmux, cursor CLI, Node.js |
tmux available on host/container; Cursor CLI can run inside tmux pane | ACP_BACKEND=cursor-tmux, optional TMUX_ACP_TSX_CLI, TMUX_ACP_SERVER_ENTRY, TMUX_ACP_SESSION_STORE, TMUX_ACP_START_COMMAND |
docker-compose -f docker/compose.yaml run --rm tmux-acp-smoke |
claude |
claude-agent-acp or bundled dist / npx fallback |
Claude Code auth must already be valid on the host; file send-back needs the bridge extension path to remain available | ACP_BACKEND=claude, optional CLAUDE_AGENT_ACP_COMMAND, CLAUDE_AGENT_ACP_EXTRA_ARGS |
docker-compose -f docker/compose.yaml run --rm claude-acp-smoke, then a real /new --backend claude ... check |
codex |
npx or local codex-acp command |
Codex auth must already be valid on the host; usually OPENAI_API_KEY or CODEX_API_KEY |
ACP_BACKEND=codex, optional CODEX_AGENT_ACP_COMMAND, CODEX_AGENT_ACP_EXTRA_ARGS |
Confirm npx @zed-industries/codex-acp works locally, then use /new --backend codex ... |
Notes:
service.shdoes not pick a backend for you; it only installs and runsnode dist/index.js.BRIDGE_WORK_ALLOWLISTis still mandatory for all backends.- Docker dev setup mirrors host tools/auth by bind-mounting local directories; if a backend works on the host but not in Docker, check mounted binaries, auth state, and path assumptions first.
The same bash service.sh script supports macOS (launchd) and Linux with systemd (e.g. Ubuntu): npm install + npm run build, then install a user service and start it. The process runs node dist/index.js with WorkingDirectory = repo root (.env is loaded by dotenv).
Same idea as feishu-cursor-claw: plist at ~/Library/LaunchAgents/com.feishu-cursor-bridge.plist, RunAtLoad, KeepAlive. Logs: /tmp/feishu-cursor-bridge.log. stop uses launchctl bootout (unloads the job but keeps the plist); killing only the process would respawn it immediately because of KeepAlive.
User unit: ~/.config/systemd/user/feishu-cursor-bridge.service, Restart=always. Logs: journalctl --user -u feishu-cursor-bridge.service (also bash service.sh logs).
To start the user service at boot without an interactive login, run once: sudo loginctl enable-linger "$USER" (optional).
bash service.sh install # npm install + build + install + start
bash service.sh update # after git pull / code edits: rebuild dist + restart
bash service.sh status
bash service.sh logs # macOS: follow log file; Linux: journalctl -f| Command | Description |
|---|---|
bash service.sh install |
npm install + npm run build, then install auto-start and launch |
bash service.sh update |
npm install + npm run build, then restart (use after git pull or local edits so dist/ matches new code) |
bash service.sh uninstall |
Remove auto-start and stop |
bash service.sh start |
Start |
bash service.sh stop |
Stop |
bash service.sh restart |
Restart the process only (no build — stale dist/ will keep running old code) |
bash service.sh status |
Status |
bash service.sh logs |
Live logs |
After code changes: prefer bash service.sh update so dependency + compile + restart happen in one step. Alternatively run npm run build then bash service.sh restart. Re-run bash service.sh install if you need to refresh the plist / systemd unit or your Node binary path changed.
The repo ships a development Docker stack under docker/: docker/Dockerfile.dev, docker/compose.yaml, docker/dev-entrypoint.sh. It does not reinstall Cursor Agent in the image; it bind-mounts these host paths:
/home/liuyang/.local/bin/home/liuyang/.local/share/cursor-agent/home/liuyang/.cursor/home/liuyang/.config/Cursor/home/liuyang/.feishu-cursor-bridge/home/liuyang/Documents
So the container can call the host’s logged-in agent acp / cursor agent and use your workspaces and local session store.
First run:
cp .env.example .env
# Edit .env
docker-compose -f docker/compose.yaml up --buildCommon commands:
docker-compose -f docker/compose.yaml up -d
docker-compose -f docker/compose.yaml restart
docker-compose -f docker/compose.yaml logs -f bridge-dev
docker-compose -f docker/compose.yaml downTwo one-off smoke services validate the cursor-tmux backend inside the container without touching a live Feishu bridge:
# Basic: newSession -> prompt -> server restart -> loadSession -> prompt -> closeSession
docker-compose -f docker/compose.yaml run --rm tmux-acp-smoke
# Cancel: session/cancel -> stopReason: cancelled
docker-compose -f docker/compose.yaml run --rm tmux-acp-cancel-smokeNotes:
- This compose targets local Linux dev; host paths are hard-coded as
/home/liuyang/...— editdocker/compose.yamlbind mounts if your machine differs. - Workspace path in the container matches the host:
/home/liuyang/Documents/feishu-cursor-bridge, so absolute paths in.envoften need no change. - Dependencies live in Docker volume
bridge_node_modules;package-lock.jsonchanges triggernpm installon container start. docker/Dockerfile.devincludestmux; Claude backend additionally requires its ACP command / auth to be available in the container. Smokes do not hold a Feishu long connection — backend regression only.
- By default the service talks to Feishu APIs and the long-connection gateway directly; no proxy is required.
- If the host must use a proxy, set
https_proxy/http_proxy/all_proxy; the Feishu long connection reuses them. - For WebSocket-specific proxy, set
wss_proxy/ws_proxy(they overridehttps_proxy/http_proxy). - When any of these is set, the service sets
NODE_USE_ENV_PROXY=1for child processes soagent/cursor-agentuse the same proxy (manual runs andsystemdboth apply). - With no proxy env vars, traffic stays direct.
- If you use
systemd --user, put proxy vars in the.envthe unit loads (orEnvironment=/EnvironmentFile=), not only in an interactive shell; the service does not inherit login shells. - For launchd / systemd --user (
service.sh), proxy settings in the project.envare loaded by the app; the service manager still does not inherit your interactive shell.
| Variable | Description | Default |
|---|---|---|
FEISHU_APP_ID |
Feishu App ID (required) | — |
FEISHU_APP_SECRET |
Feishu App Secret (required) | — |
FEISHU_DOMAIN |
feishu / lark / custom URL |
feishu |
ACP_BACKEND |
Default backend: cursor-official / cursor-legacy / cursor-tmux / claude / codex |
cursor-official |
CURSOR_AGENT_PATH |
Official ACP command path | agent |
CURSOR_API_KEY |
Official ACP API key (optional) | empty |
CURSOR_AUTH_TOKEN |
Official ACP auth token (optional) | empty |
CLAUDE_AGENT_ACP_COMMAND |
Claude ACP child command | bundled dist or npx -y @agentclientprotocol/claude-agent-acp |
CLAUDE_AGENT_ACP_EXTRA_ARGS |
Extra args appended to Claude ACP child command | empty |
CODEX_AGENT_ACP_COMMAND |
Codex ACP child command | npx -y @zed-industries/codex-acp |
CODEX_AGENT_ACP_EXTRA_ARGS |
Extra args appended to Codex ACP child command | empty |
BRIDGE_WORK_ALLOWLIST |
Required. Comma-separated absolute workspace roots; compatible with CURSOR_WORK_ALLOWLIST; ACP child cwd = first entry |
— |
CURSOR_LEGACY_NODE_PATH |
Node binary used to spawn in-repo cursor-agent-acp (cursor-legacy only; compatible with ACP_NODE_PATH) |
process.execPath |
CURSOR_LEGACY_SESSION_DIR |
Passed to cursor-agent-acp as --session-dir (cursor-legacy only; compatible with CURSOR_ACP_SESSION_DIR) |
~/.feishu-cursor-bridge/cursor-acp-sessions |
CURSOR_LEGACY_EXTRA_ARGS |
Extra args for cursor-agent-acp CLI (cursor-legacy only; compatible with CURSOR_ACP_EXTRA_ARGS) |
empty |
BRIDGE_SESSION_STORE |
Feishu ↔ ACP mapping JSON path | ~/.feishu-cursor-bridge/.feishu-bridge-sessions.json |
SESSION_IDLE_TIMEOUT_MS |
Idle before new session; 0 / infinity = never |
604800000 (7 days) |
BRIDGE_MAX_SESSIONS_PER_USER |
Max live sessions per user (all chats); 0 = unlimited |
10 |
BRIDGE_SINGLE_INSTANCE_LOCK |
Single-instance lock file (refuse start if PID alive) | ~/.feishu-cursor-bridge/bridge.lock |
BRIDGE_ALLOW_MULTIPLE_INSTANCES |
true disables single-instance lock (debug only) |
false |
FEISHU_CARD_THROTTLE_MS |
Card update throttle | 800 |
FEISHU_CARD_SPLIT_MARKDOWN_THRESHOLD |
Roll over to a new card when a card gets this long | 3500 |
FEISHU_CARD_SPLIT_TOOL_THRESHOLD |
Roll over to a new card when tool rows exceed this count | 8 |
AUTO_APPROVE_PERMISSIONS |
Auto-pick allow-style permission options; for codex, also injects sandbox_mode="danger-full-access" + approval_policy="never" at backend startup unless CODEX_AGENT_ACP_* already overrides them |
true |
LOG_LEVEL |
debug / info / warn / error |
info |
BRIDGE_DEBUG |
Verbose logs + /status details |
false |
EXPERIMENT_LOG_TO_FILE |
Experimental: append console.* to file |
false |
EXPERIMENT_LOG_FILE |
Experimental log path | ~/.feishu-cursor-bridge/logs/bridge.log |
Proxy precedence: wss_proxy / ws_proxy > https_proxy / http_proxy / all_proxy. Proxy is used only when env vars are set; otherwise direct.
- DM / group: after you create a session with
/new listthen/new <index or path>(paths must fall underBRIDGE_WORK_ALLOWLIST), normal messages go to Cursor; without a session the bot asks you to/newfirst - Incoming attachments: when a session is active, files/images/audio/video sent from Feishu are downloaded into
.feishu-incoming/under the current workspace and then described to the Agent as local paths;/filebackis the opposite direction, for asking the Agent to send workspace files back to Feishu - Group: @ the bot + content (
im:message.group_msgmust be enabled or group events won’t arrive); in topic groups, eachthread_idmaps to its own ACP session (not shared with the main group chat) - Multi-session:
/new <index or path>creates and switches (old session stays connected); bare/newlists presets;/sessionslists;/switch <index or name>switches active (no arg → last used);/closecloses one;/renamehelps name-based switching. Full syntax:docs/feishu-commands.md /statusor/状态: session stats, always shows ACP backend;cursor-official/cursor-legacy/claude/codexshow known mode for the active session;cursor-tmuxdoes not show a bridge-faked mode; recovery metadata is shown when available;cursor-officialnow shows the active ACPsessionId,claudestably shows the current Claude resume session id, andcodexshows the active ACPsessionIdby default; withBRIDGE_DEBUG=true, adds more paths, modes, and session details./mode <id>:cursor-official/cursor-legacy/claude/codexuse ACPsession/set_mode;cursor-tmuxforwards/mode ...verbatim to the real Cursor CLI pane/model <id>:cursor-legacy/cursor-official/claude/codexuse ACPsession/set_modeland support selecting from the current session's model list by 1-based index;cursor-tmuxforwards to CLI pane
npm run buildsucceeds- DM:
/new listthen/new <index or allowed path>— then send a message; card shows an “answer” block - Multi-turn — same session reused (
BRIDGE_DEBUG: stablesessionId) - With multiple
/newsessions,/switchkeeps contexts independent - When tools run, card “tools” updates (depends on Agent output)
/statusshows backend + mode; withBRIDGE_DEBUG=true,sessionId/ workspace, etc.; CLI resume ID appears only withACP_BACKEND=cursor-legacy
- Cursor
agent acp— default ACP server cursor-agent-acp/— in-repo stdio adapter when usingcursor-legacy(provenance)- @agentclientprotocol/sdk — ACP client types + connection
- @larksuiteoapi/node-sdk — Feishu long connection + message API
- TypeScript + Node.js
- Default:
cursor-official(agent acp). - Optional:
cursor-legacyuses the embeddedcursor-agent-acp/adapter;cursor-tmuxuses the tmux ACP server prototype;claudeusesclaude-agent-acp;codexuses@zed-industries/codex-acp. - Protocol follows the SDK; events cover thinking, tools, plan, mode, etc., folded by
FeishuCardState.
独立服务,通过飞书机器人统一控制多个 ACP backend。桥接进程作为 ACP Client,可将每个 session 槽位分别路由到 cursor-official、cursor-legacy、cursor-tmux、claude 或 codex。
- 飞书消息转发至 Cursor(默认经官方
agent acp) - 回复流式推送到飞书(interactive 卡片,含回答、思考、工具、计划等区块)
- 多用户会话隔离(私聊 / 群聊按用户维度映射 ACP
sessionId) - 多 session:同一用户在同一聊天中可同时持有多个 session(最多 5 个),各自独立上下文与工作区;可用
/switch在它们之间切换,未活跃的 session 仍保持 ACP 连接 - 每用户存活 session 上限:同一飞书用户跨所有私聊/群/话题的存活 session 总数默认最多 10(可用
BRIDGE_MAX_SESSIONS_PER_USER调整;0表示不限制),避免将空闲过期设为无限时进程堆积过多 ACP 连接 - 群聊 @ 机器人触发(或满足「仅 1 用户 + 1 机器人」时可免 @);私聊直接对话
- 须显式建 session:配置必填
BRIDGE_WORK_ALLOWLIST(兼容CURSOR_WORK_ALLOWLIST);先用/new list再/new <序号或路径>才能对话;裸/new等同列表 - 内置命令:
/new、/sessions、/switch、/close(含/close all)、/rename(含/new list、/new <序号>、/new <路径>等)、/status、/mode、/model;另有/topic+ 话题内容 的纯展示命令(不发给 Agent,见docs/feishu-commands.md) - 会话映射持久化:进程重启后若 Agent 声明
loadSession,可session/load恢复 - 恢复元信息:
/status会在cursor-legacy显示 CLI resume ID,在claude显示 Claude 恢复会话 id;官方 ACP 当前未暴露等价字段
飞书用户 ──(WebSocket)──> FeishuBot ──> Bridge
│
ConversationService / FeishuCardState
│
@agentclientprotocol/sdk ClientSideConnection
│
stdio NDJSON ──> agent acp 子进程(默认) / 本仓 cursor-agent-acp(`cursor-legacy`) / tmux ACP server / claude-agent-acp / codex-acp
- 飞书层:
src/feishu-bot.ts(仅 SDK 与消息收发) - ACP 运行时:
src/acp/runtime.ts+src/acp/feishu-bridge-client.ts(实现 Client:权限、沙箱读写、session/update归一化) - 编排:
src/bridge.ts、src/conversation-service.ts - 会话:
src/session-manager.ts+src/session-store.ts
- Node.js 18+
- 已安装 Cursor CLI / Agent CLI(
agent在 PATH 中,并已完成登录;若使用ACP_BACKEND=cursor-legacy,内嵌适配器会调用cursor-agent,需本机可用) - 飞书企业自建应用:机器人、
im:message、im:message.group_msg(群聊收消息必需)、im:message:send_as_bot、im:chat;若使用「仅 1 用户 + 1 机器人」免 @ 等需拉群信息的逻辑,还需按需开通im:chat相关只读权限(如查看群成员)
npm install
cp .env.example .env
# 编辑 .env
npm run dev
# (`tsx src/index.ts`:若 .env 中 ACP_BACKEND=cursor-legacy,本仓 cursor-agent-acp 默认以 tsx 跑源码,改适配器无需先 npm run build:adapter)
# 或
npm run build && npm start
# 调试:先结束已有实例再起 dev(与单实例锁配合,避免多进程)
# ./scripts/bridge-dev.sh
# npm run dev:restartbash service.sh install / update 启动的始终是同一个 bridge 进程;真正跑哪个 backend,取决于 ACP_BACKEND、/new --backend ... 以及宿主机上实际可用的命令与认证状态。
| Backend | 本机必需命令 | 额外前置条件 | 关键环境变量 | 推荐验证方式 |
|---|---|---|---|---|
cursor-official |
agent |
必要时先在宿主机执行 agent login |
ACP_BACKEND=cursor-official,可选 CURSOR_AGENT_PATH、CURSOR_API_KEY、CURSOR_AUTH_TOKEN |
先确认本地 agent acp 可用,再用 /new --backend cursor-official ... 或默认 /new |
cursor-legacy |
Node.js + 仓库内 cursor-agent-acp/ |
宿主机上的 cursor-agent / Cursor CLI 仍需可用 |
ACP_BACKEND=cursor-legacy,可选 CURSOR_LEGACY_NODE_PATH、CURSOR_LEGACY_SESSION_DIR、CURSOR_LEGACY_EXTRA_ARGS |
先确认 npm run dev / npm run build 正常,再用 /new --backend cursor-legacy ... |
cursor-tmux |
tmux、cursor CLI、Node.js |
宿主机或容器里要能启动 tmux,且 tmux pane 内可执行 Cursor CLI | ACP_BACKEND=cursor-tmux,可选 TMUX_ACP_TSX_CLI、TMUX_ACP_SERVER_ENTRY、TMUX_ACP_SESSION_STORE、TMUX_ACP_START_COMMAND |
docker-compose -f docker/compose.yaml run --rm tmux-acp-smoke |
claude |
claude-agent-acp,或 bundled dist / npx 回退 |
宿主机需已有有效 Claude Code 认证;文件回传依赖 bridge 扩展链路可用 | ACP_BACKEND=claude,可选 CLAUDE_AGENT_ACP_COMMAND、CLAUDE_AGENT_ACP_EXTRA_ARGS |
docker-compose -f docker/compose.yaml run --rm claude-acp-smoke,再补一次真实 /new --backend claude ... |
codex |
npx 或本地 codex-acp 命令 |
宿主机需已有有效 Codex/OpenAI 认证;通常依赖 OPENAI_API_KEY 或 CODEX_API_KEY |
ACP_BACKEND=codex,可选 CODEX_AGENT_ACP_COMMAND、CODEX_AGENT_ACP_EXTRA_ARGS |
先确认 npx @zed-industries/codex-acp 可用,再补一次真实 /new --backend codex ... |
说明:
service.sh不负责替你选择 backend,它只负责安装并运行node dist/index.js。- 所有 backend 都仍然要求配置
BRIDGE_WORK_ALLOWLIST。 - Docker dev setup 通过 bind mount 复用宿主机工具与认证;如果宿主机可用、容器里不可用,优先检查挂载的二进制、认证状态和绝对路径假设。
同一脚本在 macOS 使用 launchd,在 systemd 的 Linux(如 Ubuntu)使用 systemctl --user:先 npm install、npm run build,再注册用户级服务并启动。进程在仓库根目录运行 node dist/index.js(.env 由 dotenv 加载)。
与 feishu-cursor-claw 同类:~/Library/LaunchAgents/com.feishu-cursor-bridge.plist,RunAtLoad + KeepAlive。日志:/tmp/feishu-cursor-bridge.log。stop 使用 launchctl bootout(卸载任务、保留 plist);若只杀进程,KeepAlive 会让 launchd 立刻再拉起进程。
用户单元:~/.config/systemd/user/feishu-cursor-bridge.service,Restart=always。日志:journalctl --user -u feishu-cursor-bridge.service(或 bash service.sh logs)。
若要在未登录图形会话时仍随开机启动当前用户的单元,可执行一次:sudo loginctl enable-linger "$USER"(可选)。
bash service.sh install # npm install + build + 安装并启动
bash service.sh update # pull / 改代码后:install + build + 重启,使 dist 生效
bash service.sh status
bash service.sh logs # macOS:跟日志文件;Linux:journalctl -f| 命令 | 说明 |
|---|---|
bash service.sh install |
npm install + npm run build + 安装自启动并启动 |
bash service.sh update |
npm install + npm run build + 重启(已 install 后更新代码用,保证 dist/ 为新版本) |
bash service.sh uninstall |
卸载自启动并停止 |
bash service.sh start |
启动 |
bash service.sh stop |
停止 |
bash service.sh restart |
仅重启进程(不编译;dist/ 未更新则仍跑旧代码) |
bash service.sh status |
状态 |
bash service.sh logs |
实时日志 |
代码更新后优先 bash service.sh update(依赖 + 编译 + 重启一步完成)。也可手动 npm run build 再 bash service.sh restart。需要重写 plist / systemd unit 或更换 Node 路径时再执行 bash service.sh install。
仓库内提供了一套开发联调用的 Docker 配置,集中放在 docker/ 目录:docker/Dockerfile.dev、docker/compose.yaml、docker/dev-entrypoint.sh。它不会在镜像里重新安装 Cursor Agent,而是直接复用宿主机上的以下目录:
/home/liuyang/.local/bin/home/liuyang/.local/share/cursor-agent/home/liuyang/.cursor/home/liuyang/.config/Cursor/home/liuyang/.feishu-cursor-bridge/home/liuyang/Documents
这样容器里的 bridge 可以直接调用宿主机已登录的 agent acp / cursor agent,也能继续访问你的工作区与本地 session store。
首次使用:
cp .env.example .env
# 编辑 .env
docker-compose -f docker/compose.yaml up --build常用命令:
docker-compose -f docker/compose.yaml up -d
docker-compose -f docker/compose.yaml restart
docker-compose -f docker/compose.yaml logs -f bridge-dev
docker-compose -f docker/compose.yaml down当前仓库还提供了两个一次性 smoke 服务,用于在不影响宿主机正在运行的飞书 bridge 的情况下,单独验证容器内的 tmux backend:
# tmux 基本链路:newSession -> prompt -> server 重启 -> loadSession -> prompt -> closeSession
docker-compose -f docker/compose.yaml run --rm tmux-acp-smoke
# tmux 取消链路:session/cancel -> stopReason: cancelled
docker-compose -f docker/compose.yaml run --rm tmux-acp-cancel-smoke
# claude 最小链路:newSession -> prompt -> closeSession
# 需容器内可执行 CLAUDE_AGENT_ACP_COMMAND,且已具备 Claude Code / Anthropic 认证
docker-compose -f docker/compose.yaml run --rm claude-acp-smoke说明:
- 该 compose 面向本机 Linux 开发联调,当前宿主机路径按
/home/liuyang/...写死;若换机器,请同步修改docker/compose.yaml中的 bind mount。 - 容器内工作目录与宿主机保持一致:
/home/liuyang/Documents/feishu-cursor-bridge,因此现有.env里的工作区绝对路径通常无需额外改写。 - 依赖安装在 Docker volume
bridge_node_modules中;package-lock.json变化后,容器启动时会自动重新执行npm install。 docker/Dockerfile.dev现已安装tmux,因此 smoke 服务可直接验证ACP_BACKEND=cursor-tmux路径;这些 smoke 服务不会占用飞书 bot 长连接,只用于容器内的 backend 回归。
- 默认情况下服务会直接连接飞书接口与长连接网关,不要求必须配置代理。
- 若机器无法直接访问外网、只能通过代理访问飞书,可设置环境变量
https_proxy/http_proxy/all_proxy;飞书长连接也会自动复用这些代理配置。 - 若需为 WebSocket 单独指定代理,也可设置
wss_proxy/ws_proxy;其优先级高于https_proxy/http_proxy。 - 检测到上述任一代理变量时,服务会自动为子进程补上
NODE_USE_ENV_PROXY=1,使agent/cursor-agent等子进程也走环境代理;手动启动与systemd服务均适用。 - 若未设置任何代理变量,则保持直连模式。
- 若使用
systemd --user运行服务,请把代理变量写进该服务读取的.env(或 unit 的Environment=/EnvironmentFile=),不要只写在交互式 shell 配置里;服务不会自动继承登录 shell 的环境。 - 使用 launchd / systemd --user(
service.sh)时,项目.env会被进程加载;服务管理环境同样不会继承交互式 shell。
| 变量 | 说明 | 默认值 |
|---|---|---|
FEISHU_APP_ID |
飞书 App ID(必填) | - |
FEISHU_APP_SECRET |
飞书 App Secret(必填) | - |
FEISHU_DOMAIN |
feishu / lark / 自定义 URL |
feishu |
ACP_BACKEND |
ACP 后端:cursor-official / cursor-legacy / cursor-tmux / claude / codex |
cursor-official |
CURSOR_AGENT_PATH |
官方 ACP 命令路径 | agent |
CURSOR_API_KEY |
官方 ACP API key(可选) | 空 |
CURSOR_AUTH_TOKEN |
官方 ACP auth token(可选) | 空 |
CLAUDE_AGENT_ACP_COMMAND |
Claude ACP 子进程命令 | bundled dist 或 npx -y @agentclientprotocol/claude-agent-acp |
CLAUDE_AGENT_ACP_EXTRA_ARGS |
追加到 Claude ACP 子进程命令后的额外参数 | 空 |
CODEX_AGENT_ACP_COMMAND |
Codex ACP 子进程命令 | npx -y @zed-industries/codex-acp |
CODEX_AGENT_ACP_EXTRA_ARGS |
追加到 Codex ACP 子进程命令后的额外参数 | 空 |
BRIDGE_WORK_ALLOWLIST |
必填,逗号分隔的绝对路径根;兼容 CURSOR_WORK_ALLOWLIST;ACP 子进程 cwd 取列表首项 |
— |
CURSOR_LEGACY_NODE_PATH |
用于启动本仓 cursor-agent-acp 子进程的 Node(仅 cursor-legacy,兼容 ACP_NODE_PATH) |
process.execPath |
CURSOR_LEGACY_SESSION_DIR |
传给 cursor-agent-acp 的 --session-dir(仅 cursor-legacy,兼容 CURSOR_ACP_SESSION_DIR) |
~/.feishu-cursor-bridge/cursor-acp-sessions |
CURSOR_LEGACY_EXTRA_ARGS |
透传 cursor-agent-acp CLI 的额外参数(仅 cursor-legacy,兼容 CURSOR_ACP_EXTRA_ARGS,空格分隔) |
空 |
BRIDGE_SESSION_STORE |
飞书↔ACP 映射 JSON 路径 | ~/.feishu-cursor-bridge/.feishu-bridge-sessions.json |
SESSION_IDLE_TIMEOUT_MS |
空闲多久新建会话;0 / infinity 表示永不过期 |
604800000(7 天) |
BRIDGE_MAX_SESSIONS_PER_USER |
同一用户存活 session 总数上限(跨聊天);0 不限制 |
10 |
BRIDGE_SINGLE_INSTANCE_LOCK |
单实例锁文件路径(已存在且 PID 存活则拒绝启动) | ~/.feishu-cursor-bridge/bridge.lock |
BRIDGE_ALLOW_MULTIPLE_INSTANCES |
true 时禁用单实例锁(仅调试) |
false |
FEISHU_CARD_THROTTLE_MS |
卡片更新节流 | 800 |
FEISHU_CARD_SPLIT_MARKDOWN_THRESHOLD |
单张卡片内容达到该长度后滚动到下一张 | 3500 |
FEISHU_CARD_SPLIT_TOOL_THRESHOLD |
单张卡片工具条目超过该数量后滚动到下一张 | 8 |
AUTO_APPROVE_PERMISSIONS |
自动选择允许类权限选项 | true |
LOG_LEVEL |
debug / info / warn / error |
info |
BRIDGE_DEBUG |
调试日志与 /status 详情 |
false |
EXPERIMENT_LOG_TO_FILE |
实验参数:是否把 console.* 追加写入日志文件 |
false |
EXPERIMENT_LOG_FILE |
实验参数:日志文件路径 | ~/.feishu-cursor-bridge/logs/bridge.log |
代理相关说明:
wss_proxy / ws_proxy > https_proxy / http_proxy / all_proxy。仅在环境变量存在时才启用代理,否则默认直连。
- 私聊 / 群聊:须先用
/new list再/new <序号或路径>创建 session(路径须在BRIDGE_WORK_ALLOWLIST下),之后普通消息才会进 Agent;无 session 时机器人会提示先/new - 切换 backend:可用
/new <序号或路径> --backend <cursor-official|cursor-legacy|cursor-tmux|claude|codex>为新 session 指定 backend;但该 backend 必须已包含在ACP_ENABLED_BACKENDS中,否则不会被启动 - 飞书附件入站:有活跃 session 时,用户直接发送的文件 / 图片 / 音频 / 视频会先下载到当前工作区的
.feishu-incoming/,再把相对路径说明交给 Agent;/fileback则是反方向,用于让 Agent 把工作区文件回传到飞书 - 群聊:@机器人 + 内容(开发平台须为应用开通
im:message.group_msg,否则群消息事件不会投递到机器人);话题群内不同话题(thread_id)会分别映射 ACP 会话,与群主页会话互不共享 - 多 session 切换:
/new <序号或路径>新建并切到该 session(裸/new等同列表);/sessions列表;/switch <编号或名称>切换活跃 session(无参数时切到上一次用过的);/close关闭指定;/rename便于用名称切换。完整语法与快捷列表见docs/feishu-commands.md /status或/状态:会话统计,始终展示当前 ACP 后端;cursor-official/cursor-legacy/claude/codex下还会展示当前活跃 session 已知 mode,cursor-tmux下不再显示 bridge 侧伪造的 mode;若是cursor-legacy,会额外显示当前活跃 slot 的 CLI resume ID,若是cursor-official则默认显示当前 Official ACPsessionId,若是claude则稳定显示当前 Claude 恢复会话 id(新建 session 时回退为当前 ACPsessionId),若是codex则默认显示当前 ACPsessionId;BRIDGE_DEBUG=true时额外含更多 ACPsessionId、路径、可用模式等调试信息/mode <模式ID>:cursor-official/cursor-legacy/claude/codex下通过 ACPsession/set_mode切换当前活跃 session 的 mode;cursor-tmux下则把/mode ...原样发给真实 Cursor CLI pane,由 CLI 自己处理/model <模型ID>:cursor-legacy/cursor-official/claude/codex后端下通过 ACPsession/set_model切换当前活跃 session 的模型;桥接会以当前 ACP session 返回的可用模型列表为准,并统一支持桥接侧/model <序号>(1-based);cursor-tmux后端下则把/model ...原样发给真实 Cursor CLI pane,由 CLI 自己处理
npm run build通过- 私聊:先
/new list再/new <序号或允许路径>,然后发一条普通消息,卡片出现「回答」区块 - 连续多轮对话,确认复用同一会话(
BRIDGE_DEBUG下 sessionId 不变) - 若已用
/new建多个 session,可用/switch在编号间切换且各 session 独立 - 触发工具调用时,卡片「工具」列表有更新(视 Agent 输出而定)
- 发送
/status,确认显示当前 ACP 后端与当前 mode;若BRIDGE_DEBUG=true,再确认返回里包含sessionId、工作区路径等调试信息;只有切到ACP_BACKEND=cursor-legacy时,才会额外出现 CLI resume ID
- Cursor
agent acp— 默认 ACP 服务端 cursor-agent-acp/— 内嵌 stdio 适配器源码与构建产物(ACP_BACKEND=cursor-legacy);溯源见 docs/third-party.md。目录内package.json仅含"type":"commonjs",避免根目录"type":"module"将dist/*.js误判为 ESM- @agentclientprotocol/sdk — 官方 ACP Client 连接与类型
- @larksuiteoapi/node-sdk — 飞书长连接与消息 API
- TypeScript + Node.js
- 默认 backend 为
cursor-official,对应官方agent acp。 ACP_ENABLED_BACKENDS控制启动时实际启用哪些 backend;若未设置,默认只启用ACP_BACKEND当前值。- 可选 backend 包括
cursor-legacy(本仓cursor-agent-acp/)、cursor-tmux(tmux ACP server 原型)、claude(claude-agent-acp)和codex(@zed-industries/codex-acp)。 - 协议实现以 SDK 为准,事件面覆盖思考、工具、计划、模式等,并由
FeishuCardState折叠展示。