A local Feishu / Lark bot for calling Codex CLI from chat. You send a message in Feishu / Lark; the bridge runs codex exec on your machine and streams the result back to chat.
This project is built with reference to zarazhangrui/feishu-claude-code-bridge, with thanks for the original design and implementation.
You need:
- Node.js >= 20
- A local terminal for QR scan, install prompts, and
codex login - Network access to npm registry, OpenAI / Codex, and the Feishu / Lark open platform
You do not need to preinstall codex or lark-cli. When missing, the bridge installs them into private directories to avoid global npm permission issues:
~/.feishu-codex-bridge/codex-cli
~/.feishu-codex-bridge/lark-cliIf Codex is installed as a macOS app but codex is not in PATH, the bridge also checks:
/Applications/Codex.app/Contents/Resources/codexYou can pin a custom binary explicitly:
export CODEX_BIN="/path/to/codex"npm i -g feishu-codex-bridge
feishu-codex-bridge --versionRun in a local terminal:
feishu-codex-bridge startOn first run, it guides you through:
- Find Codex CLI from
CODEX_BIN,PATH, the bridge private install, or macOSCodex.app; if missing, ask to install@openai/codex. - Check Codex login status; if needed, run
codex login. - Show a QR code for Feishu / Lark scan.
- Create or select a PersonalAgent app.
- Save app config and move the App Secret into a local encrypted keystore.
- Check
lark-cli; if missing, ask to install it into the private directory. - Initialize
lark-cliwith the same App ID.
After the terminal prints that it is listening, DM the bot:
/status
Help me inspect this repo
/status is a chat-side health check. It shows the current cwd, Codex session, active agent, and reasoning effort. Regular messages are sent to Codex.
In groups and topic groups, the default policy is to respond only when mentioned.
For service mode, complete this step first; see Background Service.
The QR wizard can create the app shell, but you still need to confirm scopes and events in the developer console. Without these settings, the bridge may connect successfully while the bot still cannot receive messages or send replies.
Permission scopes:
im:messageim:message:send_as_botim:resourceim:chat, required for group creationdrive:drive, required for cloud-doc comments
Event subscriptions, long-connection mode:
im.message.receive_v1card.action.triggerdrive.notice.comment_add_v1, required for cloud-doc@botcommentsim.message.reaction.created_v1/deleted_v1, optionalim.chat.member.bot.added_v1, optional
The bridge and lark-cli must use the same Feishu / Lark app. Do not run this for lark-cli:
lark-cli config init --newOtherwise bot messages, API permissions, and OAuth identities are split across apps and become hard to debug.
Complete one foreground setup first. After the terminal is listening and /status works in chat, press Ctrl+C to stop the foreground process, then install the macOS launchd service:
feishu-codex-bridge service install launchd
feishu-codex-bridge service status
feishu-codex-bridge service logs --followRunning two bridge processes for the same Feishu / Lark app causes unreliable event delivery. Stop the foreground process before installing the service.
Restart or uninstall:
feishu-codex-bridge service restart
feishu-codex-bridge service uninstallService logs:
~/.feishu-codex-bridge/service.log~/.feishu-codex-bridge/service.err.log
The bridge keeps a local long-lived bot process and turns each chat message into a local Codex run:
Feishu/Lark chat
-> bridge WebSocket
-> local codex exec / resume
-> optional lark-cli calls
-> streaming card / text reply
Responsibilities are split:
- bridge receives events, maps chats to sessions, downloads attachments, renders cards, and keeps the bot alive.
- Codex CLI reasons, edits files, runs commands, and resumes Codex sessions.
- Lark CLI gives Codex a practical API tool for messages, docs, calendars, groups, OAuth, and other Lark resources.
Common chat commands:
| Command | Effect |
|---|---|
/status |
Show cwd, session, agent, and reasoning effort |
/new / /reset |
Reset the current chat’s Codex session |
/resume [N] |
List and resume recent Codex sessions under the current cwd |
/cd <path> |
Change cwd for the current chat and reset the session |
/ws list/save/use/remove |
Manage named workspaces |
/config |
Adjust reply mode, tools, concurrency, timeout, reasoning effort, access control |
/timeout [N|off|default] |
Override idle timeout for the current session |
/stop |
Stop the current Codex run |
/ps |
List bridge processes on this host |
/exit <id|#> |
Stop a bridge process |
/reconnect |
Reconnect the Feishu / Lark WebSocket |
/doctor [description] |
Ask Codex to diagnose recent bridge logs |
/account |
View or change the Feishu / Lark app used by the bridge |
/help |
Show the help card |
Basic chat does not require user OAuth. You only need it when Codex must access personal resources such as your own chat history, docs, or calendar:
export PATH="$HOME/.feishu-codex-bridge/lark-cli/node_modules/.bin:$PATH"
lark-cli auth login --recommendBot identity being ready does not mean user OAuth is complete. Tenant/bot APIs can work with bot identity; personal resources usually require user OAuth.
The npm package already contains dist/ and can run directly. A fresh clone needs dependencies and a build first:
npx pnpm@10.20.0 install
npx pnpm@10.20.0 build
node bin/feishu-codex-bridge.mjs --helpCommon checks:
npx pnpm@10.20.0 typecheck
npx pnpm@10.20.0 test| Path | Purpose |
|---|---|
~/.feishu-codex-bridge/config.json |
Bridge app config and preferences |
~/.feishu-codex-bridge/secrets.enc |
Encrypted App Secret store |
~/.feishu-codex-bridge/sessions.json |
Chat/topic to Codex session mapping |
~/.feishu-codex-bridge/workspaces.json |
Named workspaces |
~/.feishu-codex-bridge/processes.json |
Live bridge process registry |
~/.feishu-codex-bridge/codex-cli/ |
Bridge-managed private Codex CLI install |
~/.feishu-codex-bridge/lark-cli/ |
Bridge-managed private Lark CLI install |
~/.feishu-codex-bridge/logs/YYYY-MM-DD.log |
Structured runtime logs |
~/.feishu-codex-bridge/media/<chatId>/ |
Downloaded image/file cache, cleaned after 24h |
By default the bridge does not override Codex CLI reasoning effort. Codex inherits model_reasoning_effort from ~/.codex/config.toml.
To pin it for bridge runs only, use /config in Feishu / Lark or edit:
{
"preferences": {
"codexReasoningEffort": "xhigh"
}
}Allowed values: minimal, low, medium, high, xhigh. Remove the field, or select default in /config, to inherit the global Codex config again.
The default mode is open: anyone who can find the bot can DM it, and group users can mention it. Tighten this in /config:
allowedUsers: open_id allowlist for interacting with the bot.allowedChats: group chat_id allowlist; DMs are not restricted by this field.admins: users allowed to run sensitive commands such as/account,/config,/exit,/reconnect,/doctor,/cd, and/ws.
Find IDs from logs:
grep '"event":"enter"' ~/.feishu-codex-bridge/logs/$(date +%Y-%m-%d).log | tail -5Bot is silent
Check process and service state:
feishu-codex-bridge ps
feishu-codex-bridge service statusFollow logs:
feishu-codex-bridge service logs --followIf the bridge is connected but chat is silent, check open-platform scopes and event subscriptions first.
Codex CLI is missing or not logged in
feishu-codex-bridge doctorIf needed, rerun foreground setup:
feishu-codex-bridge startCodex cannot find lark-cli
feishu-codex-bridge doctorFor direct terminal usage:
export PATH="$HOME/.feishu-codex-bridge/lark-cli/node_modules/.bin:$PATH"lark-cli App ID differs from the bridge App ID
Rerun foreground setup:
feishu-codex-bridge startAccept the prompt to switch lark-cli back to the bridge app. Do not run lark-cli config init --new.
Codex run hangs
Send /stop in chat. For recurring hangs, set a global idle timeout in /config, or set one for the current session:
/timeout 10