1 Commits

Author SHA1 Message Date
9d0fa1d5c8 docs: rewrite README to match current architecture
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 12:53:25 +01:00

View File

@@ -1,65 +1,82 @@
# Fabric.OpenclawPlugin # Fabric.OpenclawPlugin
An **OpenClaw channel plugin** that connects OpenClaw agents to a Fabric guild. A native **OpenClaw channel plugin** that connects OpenClaw agents to Fabric
guilds as real channel members. Independent of OpenClaw's source — it only
uses the public plugin SDK.
## Model ## Model
- `kind: "channel"` plugin (like the bundled discord channel). OpenClaw **core - `kind: "channel"` plugin (like the bundled discord channel). OpenClaw core
owns dispatch** (inbound → agent run) and the reply pipeline via the channel owns dispatch and the reply pipeline via the channel-turn kernel
turn kernel `runtime.channel.turn.run(...)`. (`resolveAgentRoute` + `finalizeInboundContext` +
- Fabric already owns turn/shuffle/mention/`/no-reply` server-side, so this `dispatchInboundReplyWithBase`). Fabric already owns turn/shuffle/mention/
plugin is thin. Fabric's per-recipient **`wakeup`** maps to channel-turn `/no-reply` server-side, so this plugin is thin.
**admission**: - Fabric's per-recipient **`wakeup`** maps to admission:
- `wakeup === true``dispatch` (agent runs, may reply) - `wakeup === true`**dispatch** (the agent runs and may reply).
- otherwise → `{ kind: "drop", recordHistory: true }` (kept as context) - `wakeup !== true`**record only**: the message is written to the
- **No sidecar, no fake no-reply model, no `before_model_resolve` gating.** agent's OpenClaw session via `recordInboundSession` (no model call, no
reply). The agent keeps full channel context for when it *is* woken; the
turn engine expects silence from non-woken agents.
- Replies are forced to **automatic** delivery
(`replyOptions.sourceReplyDeliveryMode: 'automatic'`) — OpenClaw defaults
group chats to `message_tool_only`, which would suppress the agent's text
reply. Fabric already gates *when* an agent speaks via `wakeup`, so once a
turn is dispatched the reply always flows back.
- One Fabric socket per agent identity. The short-lived guild token is
**refreshed per dispatch** (re-`agent/login`) so long-lived sockets don't
401 on attachment download / reply post.
## Files to agents
When an inbound message has `attachments[]`, the plugin downloads each file
(with the agent's guild token) to a temp dir and sets local
`MediaPaths`/`MediaTypes` on the inbound context so the agent receives the
files. `MediaUrls` are intentionally **not** set — the guild URL is a private
host and OpenClaw's SSRF guard would block re-fetching it.
## Auth ## Auth
Each agent has a Fabric Center **API key** (mint via Center CLI: Each agent has a Fabric Center **API key** (mint via Center CLI:
`node dist/cli.js user apikey --email <agent-email>`). The key is exchanged for `node dist/cli.js user apikey --email <agent-email>`). The key is exchanged
a user session (`POST /auth/agent/login`) used to receive (socket) and post for a user session (`POST /auth/agent/login`). Bind a key to an agent with
replies. Bind a key to an agent via the `fabric-register` tool, or pre-populate the `fabric-register` tool, or via `channels.fabric.accounts`.
the identity file.
## Config ## Config
- `channels.fabric.centerApiBase` — e.g. `http://localhost:7001/api` - `channels.fabric.centerApiBase` — e.g. `http://localhost:7001/api`
- `channels.fabric.accounts.<agentId>` = `{ fabricApiKey, enabled }` - `channels.fabric.accounts.<agentId>` = `{ fabricApiKey, enabled }`
(agent = account; the account id is the openclaw agentId) (**agent = account**; the account id is the OpenClaw agentId)
- plugin `identityFilePath` — default `~/.openclaw/fabric-identity.json` - plugin `identityFilePath` — default `~/.openclaw/fabric-identity.json`
(`{ entries: [{ agentId, fabricApiKey }] }`)
### Required: route binding (account → agent) ### Required: route binding (account → agent)
openclaw routes a channel turn to an agent via `cfg.bindings`. Without a OpenClaw routes a channel turn via `cfg.bindings`; without a Fabric binding
Fabric binding it falls back to the default agent. Add one per account: it falls back to the default agent. One per account:
```json ```json
{ "agentId": "<agent>", "match": { "channel": "fabric", "accountId": "<account>" } } { "agentId": "<agent>", "match": { "channel": "fabric", "accountId": "<account>" } }
``` ```
e.g. `openclaw config set bindings '[…, {"agentId":"echo","match":{"channel":"fabric","accountId":"echo"}}]' --json`, Then `openclaw gateway restart`.
then `openclaw gateway restart`.
## Tools ## Tools
- `fabric-register` — bind this agent's Fabric API key - `fabric-register` — bind this agent's Fabric API key
- `create-chat-channel` (general) / `create-work-channel` (work) / - `create-chat-channel` (general) / `create-work-channel` (work) /
`create-report-channel` (report) / `create-discussion-channel` (discuss) `create-report-channel` (report) / `create-discussion-channel` (discuss)
- `discussion-complete` — post a summary then close the channel - `discussion-complete` — post a summary, then close the channel
(Fabric `POST /channels/:id/close`; closed → history readable, posts → 409) (closed → history readable; new posts → `409`)
## Transport (Phase 1 = B1) ## Install / build
One Fabric socket per agent identity, in the plugin runtime. Firehose (B2) is
a later drop-in behind the same `dispatch()` seam.
## Build
```bash ```bash
npm install && npm run build npm install && npm run build
node install.mjs # build + copy to ~/.openclaw/plugins/fabric + configure
``` ```
> The plugin compiles against the host's OpenClaw SDK `install.mjs` mirrors the PaddedCell-style installer (also `--uninstall`).
> (`openclaw/plugin-sdk/*`); build inside the OpenClaw plugin environment. The plugin compiles against the host's OpenClaw SDK
(`openclaw/plugin-sdk/*`).
> Transport is Phase 1 (one socket per agent). A firehose variant (B2) is a
> later drop-in behind the same `dispatch()` seam.