9.5 KiB
gori-agent-gateway
Hermes-gateway-style multi-IM Agent Gateway for routing instant-message webhooks to configurable CLI agents. The primary product flow is gori-agent setup: discover local agents first, pick one, then choose the IM platform to connect.
Quick start
Requires Node.js 20+.
npm install
npm run build
./install.sh
gori-agent setup
install.sh installs the command wrapper under ~/.gori-agent/bin, stores runtime PID/log files under ~/.gori-agent/state and ~/.gori-agent/logs, and adds the command to ~/.bashrc. Open a new terminal or run source ~/.bashrc before using gori-agent directly.
If the package bin has not been linked, use the no-link fallback:
npm run gori-agent -- setup
The setup wizard writes config.json only after confirmation. config.json is ignored by git and should hold local secrets.
Start the gateway after setup:
gori-agent start --config ./config.json
Fallback:
npm run gori-agent -- start --config ./config.json
Convenience executable:
gori-agent setup
gori-agent start
gori-agent start-daemon
gori-agent status
gori-agent logs
gori-agent stop
If the command is not linked into PATH, run the project-local wrapper:
./gori-agent.sh status
Use --config path after the command to override the config path.
CLI commands
gori-agent setup
gori-agent discover-agents [--json]
gori-agent start [--config path]
gori-agent status [--config path]
gori-agent doctor [--config path]
gori-agent print feishu [--config path]
gori-agent --help
Package scripts mirror common commands:
npm run setup
npm run discover-agents
npm run doctor
Agent-first setup flow
gori-agent setup does the following:
- Discovers local CLI agents on
PATHand always includes the built-inechofallback. - Lets you pick one agent.
- Lets you choose an IM platform: Feishu/Lark, WeChat external webhook, WeCom scaffold, QQ Bot webhook, or Generic webhook.
- Builds a focused
config.jsoncontaining the chosen default agent, the chosen agent plusecho, and the chosen platform enabled. - Asks before writing.
Detected ready agents with known safe prompt modes:
| Agent | Command | Args | Input mode | Permission mode |
|---|---|---|---|---|
| Kimi | kimi |
-p |
final argv | kimi -p already runs non-interactively under Kimi Code's auto permission policy. --yolo cannot be combined with -p. |
| OpenCode | opencode |
run |
final argv | setup can append --auto. |
| Codex | codex |
exec |
final argv | default; add manual args if needed. |
| Claude | claude |
-p |
final argv | default; add manual args if needed. |
| Built-in echo | node |
scripts/echo-agent.js |
stdin | n/a |
When you pick Kimi, setup asks which Kimi Code model to use. It reads your local Kimi model aliases at setup time with kimi provider list --json, so custom providers from your actual Kimi config appear in the menu. Choosing the default keeps Kimi Code's configured default_model; choosing a model writes -m <model> -p into that agent's args. You can also enter a custom model alias.
Gemini, Qwen/Qwen Code, Copilot, Pi, and Hermes are probed with --version then --help only. If found, they are shown as needs-config unless a safe prompt mode is known; the wizard asks you to confirm command, args, input mode, extra auto/yolo permission args, and working directory before enabling them.
Discovery never sends an actual prompt to an agent. Probes use child_process.spawn(..., { shell: false }), a 2s timeout, and a 4KB output cap.
Platform status
| Platform | Inbound | Outbound | Notes |
|---|---|---|---|
| Feishu/Lark | Implemented | Implemented | Replies to im.message.receive_v1 via /im/v1/messages/{message_id}/reply. |
| WeCom | 501 scaffold | Implemented | Uses gettoken and message/send; inbound callback verification/encryption is not in v1. |
| personal WeChat | External webhook scaffold | Synchronous webhook response | Native iLink/personal WeChat integration is not included in v1. |
| Implemented | Implemented | Supports QQ official Bot WebSocket gateway mode and optional HTTP callback mode. | |
| Generic webhook | Implemented | Synchronous JSON | HMAC-SHA256 signed JSON endpoint for local bridges and tests. |
Hermes reference
This project follows the Hermes gateway pattern: platform adapters normalize inbound messages, a central gateway applies policy/session/concurrency handling, then replies are sent through the originating adapter. The Feishu adapter ports the key Hermes behavior for tenant token caching, URL challenge handling, token verification, im.message.receive_v1 parsing, mention cleanup, and message replies.
Endpoints
GET /healthGET /platformsPOST /webhook/feishuPOST /webhook/wecomPOST /webhook/qqPOST /webhook/genericPOST /webhook/weixin
Configuration
Server execution still supports the existing environment variable:
GORI_GATEWAY_CONFIG=./config.json npm start
The CLI resolves config in this order:
--config pathGORI_GATEWAY_CONFIG./config.json- seed from
config.example.jsonand write to./config.jsonif confirmed
Important sections:
server.host: bind address, default0.0.0.0.server.port: gateway port, default3000.server.publicBaseUrl: public HTTPS base URL used by print/setup hints, for examplehttps://agent.example.com.policy.allowedUsers: allow only listed normalized user IDs when non-empty.policy.allowedChats: allow only listed normalized chat IDs when non-empty.policy.requireMentionInGroup: if true, group messages are ignored unless the adapter reports a bot mention.defaultAgent: selected when a chat has not chosen an agent.agents[]: named CLI agents usingchild_process.spawn(command, args)withshell: false.
CLI agent options:
inputMode: "stdin": sends the message text to stdin.inputMode: "arg": appends the message text as the final argv item.timeoutMs: kills slow processes.outputMaxBytes: caps captured stdout/stderr.cwd: optional working directory for the agent process.
Chat commands
The gateway handles these commands per chat before invoking an agent:
/help/status/agents/agent <name>/new
Feishu setup
The wizard can collect Feishu values and gori-agent print feishu --config ./config.json prints the webhook URL and checklist.
Manual steps:
- Create a Feishu/Lark custom app and enable bot messaging.
- Configure event subscription for
im.message.receive_v1. - Set the request URL to
https://<host>/webhook/feishu. - Put
appId,appSecret, andverificationTokeninto your config. - Add bot display names to
platforms.feishu.botNamesso group mention detection can also work from flattened text.
The adapter accepts Feishu URL verification challenges and returns { "challenge": "..." }.
QQ setup
QQ has two connection modes:
websocket(default/recommended): the gateway actively connects to QQ with OAuth access token and WebSocket. No public domain or callback URL is needed.webhook: QQ posts events to a public HTTPS callback URL.
Manual WebSocket steps:
- Create a QQ official Bot.
- Put
appIdandclientSecretinto your config. - Keep
platforms.qq.connectionModeas"websocket". - Keep the default
intentsvalue33554432(1 << 25,GROUP_AND_C2C_EVENT) to receiveGROUP_AT_MESSAGE_CREATEandC2C_MESSAGE_CREATE. - Run
gori-agent start --config ./config.json; the process should logQQ websocket readyafter successful authentication.
Manual HTTP callback steps:
- Set
platforms.qq.connectionModeto"webhook". - Configure the callback URL to
https://<host>/webhook/qq. QQ callback URLs must use an allowed public HTTPS port such as 443, 8443, 8080, or 80. - Put
botSecretinto your config and keepverifySignatureenabled soX-Signature-Ed25519callbacks are verified.
The webhook adapter handles QQ op: 13 callback URL validation and returns { "plain_token": "...", "signature": "..." }. GROUP_AT_MESSAGE_CREATE and C2C_MESSAGE_CREATE callbacks return QQ HTTP callback ACK { "op": 12 } immediately, then reply through the QQ Bot group/C2C message APIs.
Generic webhook
Payload:
{
"chat_id": "demo-chat",
"user_id": "demo-user",
"text": "hello",
"message_id": "optional-message-id",
"is_group": false,
"mentions_bot": true
}
Signature header:
- Header name:
X-Gori-Signature - Format:
sha256=<hex>or just<hex> - Input: exact JSON request body bytes as sent by the client
- Algorithm: HMAC-SHA256 using
platforms.webhook.secret
Example:
body='{"chat_id":"demo-chat","user_id":"demo-user","text":"/status"}'
sig=$(printf '%s' "$body" | openssl dgst -sha256 -hmac 'replace-me' -hex | awk '{print $2}')
curl -sS http://localhost:3000/webhook/generic \
-H 'Content-Type: application/json' \
-H "X-Gori-Signature: sha256=$sig" \
-d "$body"
Response shape:
{ "ok": true, "output": "...agent or command output..." }
Security notes
- Do not expose the gateway publicly without HTTPS and upstream authentication/rate limits.
- Keep platform secrets outside source control; use a copied config file or secret manager.
- CLI agents are spawned without a shell, but they can still execute arbitrary local code. Only configure trusted commands.
- Use
allowedUsersandallowedChatsin production. - Keep
requireMentionInGroupenabled for group chats to avoid accidental agent invocation. - Set conservative
timeoutMsandoutputMaxBytesvalues for external-facing agents.