feat(plugin): fabric-channel tool (members / join / leave)
One tool, three actions backed by FabricClient channelMembers (GET
/channels/:id/members -> [{userId,bypass}]), joinChannel, and new
leaveChannel (POST /channels/:id/leave).
Verified: client-level smoke against the running guild — members
initial=[tester], after join echo2 present, after leave echo2 gone.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -96,6 +96,9 @@ Then `openclaw gateway restart`.
|
|||||||
(create/replace; caller becomes sharer) · `update` (edit in place;
|
(create/replace; caller becomes sharer) · `update` (edit in place;
|
||||||
sharer-only) · `close` (remove; sharer-only). `share` needs
|
sharer-only) · `close` (remove; sharer-only). `share` needs
|
||||||
`title`/`format`(`md`|`html`|`text`)/`source`.
|
`title`/`format`(`md`|`html`|`text`)/`source`.
|
||||||
|
- `fabric-channel` — channel membership; one tool, three `action`s:
|
||||||
|
`members` (list the channel's member userIds) · `join` (this agent
|
||||||
|
joins) · `leave` (this agent leaves).
|
||||||
|
|
||||||
## Install / build
|
## Install / build
|
||||||
|
|
||||||
|
|||||||
7
dist/fabric/src/fabric-client.js
vendored
7
dist/fabric/src/fabric-client.js
vendored
@@ -68,6 +68,13 @@ export class FabricClient {
|
|||||||
joinChannel(guildEndpoint, guildToken, channelId) {
|
joinChannel(guildEndpoint, guildToken, channelId) {
|
||||||
return this.post(`${guildEndpoint}/api/channels/${channelId}/join`, {}, guildToken);
|
return this.post(`${guildEndpoint}/api/channels/${channelId}/join`, {}, guildToken);
|
||||||
}
|
}
|
||||||
|
leaveChannel(guildEndpoint, guildToken, channelId) {
|
||||||
|
return this.post(`${guildEndpoint}/api/channels/${channelId}/leave`, {}, guildToken);
|
||||||
|
}
|
||||||
|
// [{ userId, bypass }] — bypass is true only for discuss/work bypass-list
|
||||||
|
channelMembers(guildEndpoint, guildToken, channelId) {
|
||||||
|
return this.req('GET', `${guildEndpoint}/api/channels/${channelId}/members`, guildToken);
|
||||||
|
}
|
||||||
// ---- channel canvas (one pinned doc per channel) ----
|
// ---- channel canvas (one pinned doc per channel) ----
|
||||||
canvasUrl(endpoint, channelId) {
|
canvasUrl(endpoint, channelId) {
|
||||||
return `${endpoint}/api/channels/${channelId}/canvas`;
|
return `${endpoint}/api/channels/${channelId}/canvas`;
|
||||||
|
|||||||
39
dist/fabric/src/tools.js
vendored
39
dist/fabric/src/tools.js
vendored
@@ -163,4 +163,43 @@ export function registerFabricTools(api, client, identity) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
// fabric-channel: channel membership (one tool, three actions).
|
||||||
|
api.registerTool((ctx) => ({
|
||||||
|
name: 'fabric-channel',
|
||||||
|
description: 'Channel membership. action: members (list channel member userIds) | ' +
|
||||||
|
'join (this agent joins the channel) | leave (this agent leaves).',
|
||||||
|
parameters: {
|
||||||
|
type: 'object',
|
||||||
|
additionalProperties: false,
|
||||||
|
required: ['action', 'guildNodeId', 'channelId'],
|
||||||
|
properties: {
|
||||||
|
action: { type: 'string', enum: ['members', 'join', 'leave'] },
|
||||||
|
guildNodeId: { type: 'string' },
|
||||||
|
channelId: { type: 'string' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
execute: async (p) => {
|
||||||
|
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 'members': {
|
||||||
|
const members = await client.channelMembers(ep, token, p.channelId);
|
||||||
|
return { ok: true, members };
|
||||||
|
}
|
||||||
|
case 'join': {
|
||||||
|
await client.joinChannel(ep, token, p.channelId);
|
||||||
|
return { ok: true, joined: true };
|
||||||
|
}
|
||||||
|
case 'leave': {
|
||||||
|
await client.leaveChannel(ep, token, p.channelId);
|
||||||
|
return { ok: true, left: true };
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return { ok: false, error: `unknown action ${String(p.action)}` };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,6 +111,23 @@ export class FabricClient {
|
|||||||
return this.post(`${guildEndpoint}/api/channels/${channelId}/join`, {}, guildToken);
|
return this.post(`${guildEndpoint}/api/channels/${channelId}/join`, {}, guildToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
leaveChannel(guildEndpoint: string, guildToken: string, channelId: string): Promise<unknown> {
|
||||||
|
return this.post(`${guildEndpoint}/api/channels/${channelId}/leave`, {}, guildToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
// [{ userId, bypass }] — bypass is true only for discuss/work bypass-list
|
||||||
|
channelMembers(
|
||||||
|
guildEndpoint: string,
|
||||||
|
guildToken: string,
|
||||||
|
channelId: string,
|
||||||
|
): Promise<Array<{ userId: string; bypass?: boolean }>> {
|
||||||
|
return this.req(
|
||||||
|
'GET',
|
||||||
|
`${guildEndpoint}/api/channels/${channelId}/members`,
|
||||||
|
guildToken,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// ---- channel canvas (one pinned doc per channel) ----
|
// ---- channel canvas (one pinned doc per channel) ----
|
||||||
|
|
||||||
private canvasUrl(endpoint: string, channelId: string): string {
|
private canvasUrl(endpoint: string, channelId: string): string {
|
||||||
|
|||||||
44
src/tools.ts
44
src/tools.ts
@@ -199,4 +199,48 @@ export function registerFabricTools(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// fabric-channel: channel membership (one tool, three actions).
|
||||||
|
api.registerTool((ctx: Ctx) => ({
|
||||||
|
name: 'fabric-channel',
|
||||||
|
description:
|
||||||
|
'Channel membership. action: members (list channel member userIds) | ' +
|
||||||
|
'join (this agent joins the channel) | leave (this agent leaves).',
|
||||||
|
parameters: {
|
||||||
|
type: 'object',
|
||||||
|
additionalProperties: false,
|
||||||
|
required: ['action', 'guildNodeId', 'channelId'],
|
||||||
|
properties: {
|
||||||
|
action: { type: 'string', enum: ['members', 'join', 'leave'] },
|
||||||
|
guildNodeId: { type: 'string' },
|
||||||
|
channelId: { type: 'string' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
execute: async (p: {
|
||||||
|
action: 'members' | 'join' | 'leave';
|
||||||
|
guildNodeId: string;
|
||||||
|
channelId: 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 'members': {
|
||||||
|
const members = await client.channelMembers(ep, token, p.channelId);
|
||||||
|
return { ok: true, members };
|
||||||
|
}
|
||||||
|
case 'join': {
|
||||||
|
await client.joinChannel(ep, token, p.channelId);
|
||||||
|
return { ok: true, joined: true };
|
||||||
|
}
|
||||||
|
case 'leave': {
|
||||||
|
await client.leaveChannel(ep, token, p.channelId);
|
||||||
|
return { ok: true, left: true };
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return { ok: false, error: `unknown action ${String(p.action)}` };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user