Tools
Lark Plus
扫码即加,一台 OpenClaw 驻扎多个飞书机器人。每个机器人拥有独立的 agent、工作空间和对话记忆,彼此互不可见 —— 放心和团队成员或家人共享你的 OpenClaw。
Install
npm install
```
Configuration Example
{
"plugins": {
"allow": ["openclaw-lark-plus"],
"installs": {
"openclaw-lark-plus": {
"path": "~/.openclaw/extensions/openclaw-lark-plus"
}
}
}
}
README
# openclaw-lark-plus
English | [简体中文](./README.zh-CN.md)
Multi-user fork of `@larksuite/openclaw-lark` — each Feishu user scans a QR code to get their **own independent Feishu bot**, with their own agent, conversation history, and workspace directory.
## Why
The official `@larksuite/openclaw-lark` plugin is single-owner: only the app creator can complete OAuth, and only one Feishu bot is configured per OpenClaw instance.
`openclaw-lark-plus` solves this by:
- **QR-code registration** — new users scan a QR code to auto-create an independent Feishu "Personal Agent" app via Feishu's `/oauth/v1/app/registration` API. No manual app creation, no client_id/secret copy-paste.
- **Admin approval** — the first user becomes admin; subsequent registrations require admin approval from inside Feishu.
- **Per-user agent + workspace** — each registered user gets their own entry in `config.agents.list` and a matching `config.bindings` rule, so the OpenClaw framework natively routes their messages to a per-user agent with its own workspace directory (`~/.openclaw/workspaces/<user>`), conversation history, memory, and skills.
- **Conversation isolation** — built on the SDK's `sessionKey` (scoped to agent + channel + peer), each user's chat history is naturally isolated from everyone else.
- **Coexists with the original plugin** — plugin ID is `openclaw-lark-plus`, so you can keep `@larksuite/openclaw-lark` installed side-by-side during migration.
## Prerequisites
- [OpenClaw](https://www.npmjs.com/package/openclaw) installed and running (`openclaw gateway` service up)
- Node.js >= 18
- A Feishu / Lark account to scan the first QR code and become admin
## Installation
### 1. Clone the plugin into the extensions directory
```bash
mkdir -p ~/.openclaw/extensions
cd ~/.openclaw/extensions
git clone https://github.com/shyrock/openclaw-lark-plus.git
cd openclaw-lark-plus
npm install
```
### 2. Enable the plugin in `~/.openclaw/openclaw.json`
```json
{
"plugins": {
"allow": ["openclaw-lark-plus"],
"installs": {
"openclaw-lark-plus": {
"path": "~/.openclaw/extensions/openclaw-lark-plus"
}
}
}
}
```
> The built-in `@openclaw/feishu` channel driver handles the actual Feishu WebSocket connection — `openclaw-lark-plus` layers multi-user management, QR registration, and approval flow on top of it. Both can be enabled simultaneously.
### 3. Restart the gateway
```bash
systemctl restart openclaw-gateway # or however you run the gateway
```
## Bootstrapping the first admin
There's a chicken-and-egg problem: `/feishu register` is a chat command, but there's no Feishu bot configured yet to receive commands. Use the **CLI install** command for the very first user:
```bash
openclaw lark-plus-install
```
This prints a QR code in the terminal. Scan it with Feishu/Lark:
1. Feishu creates a new "Personal Agent" app tied to your account
2. The CLI polls until authorization completes, then receives the new `client_id` / `client_secret`
3. The scanning user is recorded as **admin** in `~/.openclaw/openclaw.json`
4. An `accounts` entry, an `agents.list` entry, a per-user `bindings` rule, and a workspace directory (`~/.openclaw/workspaces/<admin-openId>`) are all written automatically
5. Restart the gateway — your personal Feishu bot is now online
From this point on, you can DM your new bot directly.
## Adding more users
Once the admin's bot is online, **in a DM with the admin's bot**, run:
```
/feishu register
```
The bot replies with a QR-code image. Share it with the new user (forward the image, screenshot, etc.).
When the new user scans:
1. Feishu creates another independent Personal Agent app for them
2. The plugin stores the registration as **pending** (credentials are held in `~/.openclaw/openclaw.json` under `channels.feishu.plus.pendingRegistrations`)
3. The admin receives a Feishu notification with the pending ID and two quick-reply commands
Admin approves or rejects:
```
/feishu approve reg-xxxxx
/feishu reject reg-xxxxx
```
On approval, the plugin writes a full account for the new user (account + agent + binding + workspace directory) and tells the admin to restart the gateway. On rejection, the pending credentials are discarded.
```bash
systemctl restart openclaw-gateway
```
After restart, the new user can DM **their own bot** directly.
## Commands
All commands below are invoked inside a Feishu chat with a `openclaw-lark-plus`-managed bot.
| Command | Who | Description |
|---------|-----|-------------|
| `/feishu register` | admin | Generate a QR code to invite a new user |
| `/feishu approve <pending_id>` | admin | Approve a pending registration |
| `/feishu reject <pending_id>` | admin | Reject & discard a pending registration |
| `/feishu pending` | admin | List all pending registrations |
| `/feishu users` | admin | List authorized users and their account/agent mapping |
| `/feishu doctor` | anyone | Run configuration diagnostics |
| `/feishu help` | anyone | Show help |
Plus the terminal-only bootstrap:
| CLI | Description |
|-----|-------------|
| `openclaw lark-plus-install` | Interactive QR flow to register the first admin user |
## How per-user isolation works
`openclaw-lark-plus` does **not** ship a custom routing layer. It writes directly into OpenClaw's native config schema so the framework does the routing for you:
**1. One `accounts` entry per user** (under `channels.feishu.accounts`) — each user has their own `appId`/`appSecret` and a `dmPolicy: "allowlist"` restricted to that user's own `openId`.
**2. One `agents.list` entry per user** — the agent id matches the user's `openId`, with a unique `workspace` directory.
**3. One `bindings` rule per user** — `{type: "route", agentId: <openId>, match: {channel: "feishu", peer: {kind: "direct", id: <openId>}}}` — tells the framework's `resolveAgentRoute` to send this user's DMs to their own agent.
**4. A default `main` agent** is always injected at the front of `agents.list` as a safety net, so any un-bound traffic (e.g. channel-wide legacy bindings) routes to a real default agent instead of silently falling through to someone else's private agent.
Because each user gets a unique `agentId`, the framework's session key becomes:
```
agent:<user-openId>:feishu:direct:<user-openId>
```
…which means **conversation history, memory, and workspace state are naturally isolated per user**. No two users share a session.
### Auto-generated config shape
After one admin + one regular user are registered, `~/.openclaw/openclaw.json` looks roughly like this:
```json
{
"plugins": {
"allow": ["openclaw-lark-plus"],
"installs": {
"openclaw-lark-plus": { "path": "~/.openclaw/extensions/openclaw-lark-plus" }
}
},
"channels": {
"feishu": {
"accounts": {
"ou_alice": {
"appId": "cli_alice_xxx",
"appSecret": "***",
"enabled": true,
"dmPolicy": "allowlist",
"allowFrom": ["ou_alice"]
},
"ou_bob": {
"appId": "cli_bob_xxx",
"appSecret": "***",
"enabled": true,
"dmPolicy": "allowlist",
"allowFrom": ["ou_bob"]
}
},
"plus": {
"adminOpenId": "ou_alice",
"adminAccountId": "ou_alice"
}
}
},
"agents": {
"list": [
{ "id": "main", "name": "main", "default": true },
{ "id": "ou_alice", "name": "ou_alice", "workspace": "/root/.openclaw/workspaces/ou_alice" },
{ "id": "ou_bob", "name": "ou_bob", "workspace": "/root/.openclaw/workspaces/ou_bob" }
]
},
"bindings": [
{
"type": "route",
"agentId": "ou_alice",
"match": { "channel": "feishu", "peer": { "kind": "direct", "id": "ou_alice" } }
},
{
"type": "route",
"agentId": "ou_bob",
"match": { "channel": "feishu", "peer": { "kind": "direct", "id": "ou_bob" } }
}
]
}
```
You can hand-edit any agent's `workspace`, `systemPromptOverride`, `skills`, `model`, etc. — those are standard OpenClaw `agents.list` fields, documented in the OpenClaw SDK.
## Architecture
```
/feishu register
│
▼
Feishu /oauth/v1/app/registration (init → begin → poll)
│
▼
new user scans QR
│
┌─────┴─────┐
│ │
First user Subsequent
│ │
│ ▼
│ store as pending
│ notify admin
│ │
│ ┌─────┴─────┐
│ ▼ ▼
│ approve reject
│ │ │
▼ ▼ ▼
└─► write account + agent + binding + workspace
│
▼
restart gateway → new bot online
```
### Key files
| File | Purpose |
|------|---------|
| `src/plugin/src/core/accounts-manager.js` | Multi-account CRUD, admin tracking, pending storage, and auto-injection of `agents.list` + `bindings` + the `main` safety-net agent |
| `src/plugin/src/core/app-registration.js` | Feishu App Registration API client (init / begin / poll) |
| `src/plugin/src/commands/register.js` | `/feishu register`, `/feishu approve`, `/feishu reject`, `/feishu pending` |
| `src/plugin/src/commands/cli-install.js` | Terminal-based first-admin bootstrap (`openclaw lark-plus-install`) |
| `src/plugin/src/commands/users.js` | `/feishu users` — list registered accounts and their bindings |
| `src/plugin/index.js` | Plugin entrypoint; registers commands and validates config |
## Security
- **Admin approval gate** — only the first registered user can approve new registrations
- **Config file is mode 0600** — credentials written to `~/.openclaw/openclaw.json` are readable only by the owning Unix user
- **Rejected credentials are discarded** — `/feishu reject` deletes the pending entry entirely
- **Per-bot DM allowlist** — each user's bot has `dmPolicy: "allowlist"` with only that user's `openId`, so bots cannot be spoken to by anyone else
- **OAuth token scope isolation** — the OpenClaw SDK stores tokens keyed by `{appId}:{userOpenId}`, so cross-
... (truncated)
tools
Comments
Sign in to leave a comment