Create a nuntius.toml file in the project root or point NUNTIUS_CONFIG_PATH at one.
Example: config/nuntius.example.toml
Minimal Feishu section:
[feishu]
app_id = "cli_xxxxxxxxxxxxxxxx"
app_secret = "your-feishu-app-secret"
allowed_open_ids = ["ou_xxxxxxxxxxxxxxxx"]
admin_open_ids = ["ou_xxxxxxxxxxxxxxxx"]Optional:
[feishu]
allow_process_restart = false
api_base_url = "https://open.feishu.cn/open-apis"For a reloadable repo registry, point the bridge at a TOML file:
[bridge]
repository_registry_path = "config/repository-registry.toml"Example format: config/repository-registry.example.toml
By default the bridge runs Codex in bridge-level yolo mode:
[bridge]
yolo_mode = trueWhen bridge.yolo_mode is true or omitted, all handler and worker turns are forced to danger-full-access with approvalPolicy: never.
Set bridge.yolo_mode = false if you want bridge.handler_sandbox_mode plus each repository target's sandbox_mode and approval_policy settings to take effect.
Repository targets also default to allow_codex_network_access = true, so worker turns request web access with codex --search unless you disable that per repository.
bridge.progress_updates defaults to minimal, which keeps intermediate replies sparse and prefers working placeholders/heartbeats when available. Set it to latest to keep one editable tool-count message plus one editable latest-status message, with the final reply overwriting only the second message; use verbose to surface more per-step progress messages, or off to wait for the final reply.
Enable bot capability for the app, then publish a version after changing scopes or event settings.
Recommended permissions:
im:message.p2p_msg:readonlyim:message.group_at_msg:readonlyim:message:sendim:message:updateim:message.reactions:write_onlyim:resource
im:resource is needed if you want the bot to download inbound file attachments from Feishu and upload modified files back into the conversation. The upload API also documents the newer im:resource:upload scope, but im:resource covers the end-to-end attachment flow used by nuntius.
Event subscription:
- Subscribe to
im.message.receive_v1 - Configure subscription mode as "Receive events through long connection"
- No callback URL or health check endpoint is required
- Long connection mode only supports event subscriptions, which is sufficient for nuntius
npm run build
npm run feishu:startThe process only needs outbound network access to Feishu. You do not need a public HTTP endpoint or intranet tunneling.
Or run all configured IM integrations together:
npm run build
npm run startTo hand the process off to a transient user service when the host allows it:
NUNTIUS_LAUNCH_MODE=systemd-run npm run feishu:startOr for the combined launcher:
NUNTIUS_LAUNCH_MODE=systemd-run npm run startThis requires systemd-run --user. If host policy blocks transient user services, nuntius exits with a clear error instead of pretending the process stayed alive.
/codex <message>/codex bind <repo-id> [message]/codex status/codex repos/codex tasks/codex reset [worker|binding|context|all]/codex interrupt/codex help
/codexadmin status/codexadmin reloadconfig/codexadmin hotreload/codexadmin restart/codexadmin help
- DM the bot directly to keep one Codex conversation per p2p chat
- Mention the bot in a group message to create a dedicated Feishu thread automatically
- Reply inside that Feishu thread to continue the same handler or worker session
- nuntius tells Codex to avoid Markdown tables in chat replies and prefer short paragraphs, lists, and code blocks that read cleanly in IM clients
- In a DM or an already-bound thread, send a
docordocxfile and then tell Codex what to change - If you send the attachment first with no text, nuntius stores it in the conversation and waits for your next text instruction before starting Codex work
- nuntius downloads the attachment to a local working path and exposes that path to Codex for the turn
- If Codex modifies an attached
.docor.docxin place, or writes a new.doc/.docxbeside it in the same attachment directory, nuntius uploads that file back to Feishu as a file message - Feishu file uploads are limited to 30 MB; larger returned documents will fail to upload and the bot will post an error in the thread instead
- If
feishu.allowed_open_idsis set, only those Feishu users can talk to the bot. - If
feishu.admin_open_idsis set, only those users can run/codexadmin. - Root group messages only trigger the bot when they start with
/codexor mention the bot. - Persistent work in a group is moved into a Feishu thread automatically; later replies in that thread reuse the same bound worker session.
/codex bind <repo-id> <message>binds the thread first and then sends<message>straight to that repository worker in the same turn.- In an unbound top-level conversation, plain text like
create a task running per hour in arbiterolets the handler create a scheduled background task under.nuntius/scheduled-tasks/<task-id>/for that repository without binding the thread. - Feishu delivery rewrites leftover Markdown links, headings, emphasis, and tables into plain-text-friendly output before posting the message.
- nuntius adds status reactions to inbound Feishu messages when the bot can address the source message directly.
- File attachments received in a conversation remain available to later turns in that conversation unless you clear the binding with
/codex reset bindingor/codex reset all. /codexadmin reloadconfigreloads the bridge config and repository registry in-process./codexadmin hotreloadrunsnpm run build, probes the rebuilt Feishu worker, then swaps the active worker when the bot is running under the bundled supervisor started bynpm run feishu:startornpm run start./codexadmin restartonly exits the process. Use systemd, Docker restart policy, or another supervisor to bring it back up.- There is no Feishu callback URL to keep in sync because events are received over the SDK's long connection client.