feat(plugin): fabric-canvas tool; fabric-register env=AGENT_ID only
- 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>
This commit is contained in:
79
src/tools.ts
79
src/tools.ts
@@ -120,4 +120,83 @@ export function registerFabricTools(
|
||||
return { ok: true, closed: true };
|
||||
},
|
||||
}));
|
||||
|
||||
// fabric-canvas: share / update / read / close the channel's single
|
||||
// pinned canvas document (one tool, four actions). update/close are
|
||||
// sharer-only server-side (the guild returns 403 otherwise).
|
||||
api.registerTool((ctx: Ctx) => ({
|
||||
name: 'fabric-canvas',
|
||||
description:
|
||||
"Manage a channel's pinned canvas document. action: " +
|
||||
"read (current canvas or null) | share (create/replace; you become " +
|
||||
'the sharer) | update (edit in place; sharer only) | close (remove; ' +
|
||||
'sharer only).',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
required: ['action', 'guildNodeId', 'channelId'],
|
||||
properties: {
|
||||
action: { type: 'string', enum: ['read', 'share', 'update', 'close'] },
|
||||
guildNodeId: { type: 'string' },
|
||||
channelId: { type: 'string' },
|
||||
title: { type: 'string', description: 'share: required; update: optional' },
|
||||
format: {
|
||||
type: 'string',
|
||||
enum: ['md', 'html', 'text'],
|
||||
description: 'share: required; update: optional',
|
||||
},
|
||||
source: {
|
||||
type: 'string',
|
||||
description: 'document body. share: required; update: optional',
|
||||
},
|
||||
},
|
||||
},
|
||||
execute: async (p: {
|
||||
action: 'read' | 'share' | 'update' | 'close';
|
||||
guildNodeId: string;
|
||||
channelId: string;
|
||||
title?: string;
|
||||
format?: 'md' | 'html' | 'text';
|
||||
source?: string;
|
||||
}) => {
|
||||
const agentId = ctx.agentId;
|
||||
if (!agentId) return { ok: false, error: 'no agent context' };
|
||||
const { guild, token } = await ctxGuild(agentId, p.guildNodeId);
|
||||
const ep = guild.endpoint;
|
||||
switch (p.action) {
|
||||
case 'read': {
|
||||
const canvas = await client.getCanvas(ep, token, p.channelId);
|
||||
return { ok: true, canvas };
|
||||
}
|
||||
case 'share': {
|
||||
if (!p.title || !p.format || p.source === undefined) {
|
||||
return { ok: false, error: 'share requires title, format, and source' };
|
||||
}
|
||||
const canvas = await client.shareCanvas(ep, token, p.channelId, {
|
||||
title: p.title,
|
||||
format: p.format,
|
||||
source: p.source,
|
||||
});
|
||||
return { ok: true, canvas };
|
||||
}
|
||||
case 'update': {
|
||||
const body: Partial<{ title: string; format: 'md' | 'html' | 'text'; source: string }> = {};
|
||||
if (p.title !== undefined) body.title = p.title;
|
||||
if (p.format !== undefined) body.format = p.format;
|
||||
if (p.source !== undefined) body.source = p.source;
|
||||
if (Object.keys(body).length === 0) {
|
||||
return { ok: false, error: 'update needs at least one of title/format/source' };
|
||||
}
|
||||
const canvas = await client.updateCanvas(ep, token, p.channelId, body);
|
||||
return { ok: true, canvas };
|
||||
}
|
||||
case 'close': {
|
||||
await client.removeCanvas(ep, token, p.channelId);
|
||||
return { ok: true, removed: true };
|
||||
}
|
||||
default:
|
||||
return { ok: false, error: `unknown action ${String(p.action)}` };
|
||||
}
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user