diff --git a/README.md b/README.md index 492cb8f..e73ae45 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,9 @@ Then `openclaw gateway restart`. (create/replace; caller becomes sharer) · `update` (edit in place; sharer-only) · `close` (remove; sharer-only). `share` needs `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 diff --git a/dist/fabric/src/fabric-client.js b/dist/fabric/src/fabric-client.js index 99a30af..46c74a2 100644 --- a/dist/fabric/src/fabric-client.js +++ b/dist/fabric/src/fabric-client.js @@ -68,6 +68,13 @@ export class FabricClient { joinChannel(guildEndpoint, guildToken, channelId) { 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) ---- canvasUrl(endpoint, channelId) { return `${endpoint}/api/channels/${channelId}/canvas`; diff --git a/dist/fabric/src/tools.js b/dist/fabric/src/tools.js index 80b1c52..04b03df 100644 --- a/dist/fabric/src/tools.js +++ b/dist/fabric/src/tools.js @@ -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)}` }; + } + }, + })); } diff --git a/src/fabric-client.ts b/src/fabric-client.ts index 6bae623..0a92bff 100644 --- a/src/fabric-client.ts +++ b/src/fabric-client.ts @@ -111,6 +111,23 @@ export class FabricClient { return this.post(`${guildEndpoint}/api/channels/${channelId}/join`, {}, guildToken); } + leaveChannel(guildEndpoint: string, guildToken: string, channelId: string): Promise { + 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> { + return this.req( + 'GET', + `${guildEndpoint}/api/channels/${channelId}/members`, + guildToken, + ); + } + // ---- channel canvas (one pinned doc per channel) ---- private canvasUrl(endpoint: string, channelId: string): string { diff --git a/src/tools.ts b/src/tools.ts index 217db53..a69df24 100644 --- a/src/tools.ts +++ b/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)}` }; + } + }, + })); }