- bin/fabric-register.mjs: only AGENT_ID is read from the environment; --api-key is flag-only (no FABRIC_API_KEY); dropped FABRIC_CENTER_API_BASE / FABRIC_IDENTITY_FILE / OPENCLAW_PATH env fallbacks (flags + sensible defaults; --center still falls back to openclaw.json). - New fabric-canvas tool (one tool, four actions): read / share / update / close the channel's single pinned canvas. Backed by FabricClient get/share/update/removeCanvas (GET/PUT/PATCH/DELETE; empty 2xx body -> null). update/close are sharer-only server-side. - README updated. Verified: client-level smoke against the running guild — read(empty→null) → share(v1) → read → update(v2) → close(→null) all pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
113 lines
4.8 KiB
Markdown
113 lines
4.8 KiB
Markdown
# Fabric.OpenclawPlugin
|
|
|
|
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
|
|
|
|
- `kind: "channel"` plugin (like the bundled discord channel). OpenClaw core
|
|
owns dispatch and the reply pipeline via the channel-turn kernel
|
|
(`resolveAgentRoute` + `finalizeInboundContext` +
|
|
`dispatchInboundReplyWithBase`). Fabric already owns turn/shuffle/mention/
|
|
`/no-reply` server-side, so this plugin is thin.
|
|
- Fabric's per-recipient **`wakeup`** maps to admission:
|
|
- `wakeup === true` → **dispatch** (the agent runs and may reply).
|
|
- `wakeup !== true` → **record only**: the message is written to the
|
|
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
|
|
|
|
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 a user session (`POST /auth/agent/login`).
|
|
|
|
### Binding a key to an agent (one-time)
|
|
|
|
Two ways, both write the same identity registry the transport reads:
|
|
|
|
1. **Static config** — set `channels.fabric.accounts.<agentId> =
|
|
{ fabricApiKey, enabled }`. The agent never runs anything.
|
|
2. **`fabric-register` script** — installed to `~/.openclaw/bin/fabric-register`
|
|
by the installer (it is **not** an OpenClaw tool):
|
|
|
|
```bash
|
|
# agent id from $AGENT_ID (set in the agent runtime):
|
|
~/.openclaw/bin/fabric-register --api-key fak_…
|
|
# or pass it explicitly:
|
|
~/.openclaw/bin/fabric-register --agent-id <agent> --api-key fak_…
|
|
```
|
|
|
|
It validates the key against Center, then writes
|
|
`~/.openclaw/fabric-identity.json`. One-time and persistent — *not* per
|
|
login; the plugin's transport logs in and stays connected on its own.
|
|
**Only `AGENT_ID` is read from the environment** — if unset, `--agent-id`
|
|
is required. `--api-key` is flag-only (never from the env). Other flags:
|
|
`--center`, `--identity-file`, `--openclaw-path` (sensible defaults;
|
|
`--center` also falls back to `openclaw.json`). Restart the gateway
|
|
afterwards.
|
|
|
|
## Config
|
|
|
|
- `channels.fabric.centerApiBase` — e.g. `http://localhost:7001/api`
|
|
- `channels.fabric.accounts.<agentId>` = `{ fabricApiKey, enabled }`
|
|
(**agent = account**; the account id is the OpenClaw agentId)
|
|
- plugin `identityFilePath` — default `~/.openclaw/fabric-identity.json`
|
|
|
|
### Required: route binding (account → agent)
|
|
|
|
OpenClaw routes a channel turn via `cfg.bindings`; without a Fabric binding
|
|
it falls back to the default agent. One per account:
|
|
|
|
```json
|
|
{ "agentId": "<agent>", "match": { "channel": "fabric", "accountId": "<account>" } }
|
|
```
|
|
|
|
Then `openclaw gateway restart`.
|
|
|
|
## Tools
|
|
|
|
(Key binding is **not** a tool — see *Binding a key to an agent* above.)
|
|
|
|
- `create-chat-channel` (general) / `create-work-channel` (work) /
|
|
`create-report-channel` (report) / `create-discussion-channel` (discuss)
|
|
- `discussion-complete` — post a summary, then close the channel
|
|
(closed → history readable; new posts → `409`)
|
|
- `fabric-canvas` — manage a channel's single pinned canvas document; one
|
|
tool, four `action`s: `read` (current canvas or null) · `share`
|
|
(create/replace; caller becomes sharer) · `update` (edit in place;
|
|
sharer-only) · `close` (remove; sharer-only). `share` needs
|
|
`title`/`format`(`md`|`html`|`text`)/`source`.
|
|
|
|
## Install / build
|
|
|
|
```bash
|
|
npm install && npm run build
|
|
node install.mjs # build + copy to ~/.openclaw/plugins/fabric + configure
|
|
```
|
|
|
|
`install.mjs` mirrors the PaddedCell-style installer (also `--uninstall`).
|
|
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.
|