From e36660238df553b3a606eefc76cb283cac38d4c7 Mon Sep 17 00:00:00 2001 From: Saul Carlin Date: Thu, 30 Apr 2026 10:46:47 -0700 Subject: [PATCH 1/4] feat(conversation): add 'add-admin' command Co-Authored-By: Claude Sonnet 4.6 --- src/commands/conversation/add-admin.ts | 49 ++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/commands/conversation/add-admin.ts diff --git a/src/commands/conversation/add-admin.ts b/src/commands/conversation/add-admin.ts new file mode 100644 index 0000000..7d02880 --- /dev/null +++ b/src/commands/conversation/add-admin.ts @@ -0,0 +1,49 @@ +import { Args } from "@oclif/core"; +import { requireGroup } from "../../utils/xmtp.js"; +import { ConvosBaseCommand } from "../../baseCommand.js"; +import { createClientForIdentity } from "../../utils/client.js"; +import { createIdentityStore } from "../../utils/identities.js"; + +export default class ConversationAddAdmin extends ConvosBaseCommand { + static description = `Promote members to admin by inbox ID. Requires super admin permissions.`; + + static strict = false; + + static args = { + id: Args.string({ description: "The conversation ID", required: true }), + }; + + static flags = { ...ConvosBaseCommand.baseFlags }; + + async run(): Promise { + const { args, argv } = await this.parse(ConversationAddAdmin); + const inboxIds = (argv as string[]).slice(1); + if (inboxIds.length === 0) this.error("At least one inbox ID is required"); + + const config = this.getConvosConfig(); + const store = createIdentityStore(this.getConvosHome()); + const identity = store.getByConversationId(args.id); + if (!identity) this.error(`No identity found for conversation: ${args.id}`); + + const client = await createClientForIdentity(identity, config, this.getConvosHome()); + const conversation = await client.conversations.getConversationById(args.id); + if (!conversation) this.error(`Conversation not found: ${args.id}`); + + const group = requireGroup(conversation); + + const promoted: string[] = []; + for (const inboxId of inboxIds) { + await group.addAdmin(inboxId); + promoted.push(inboxId); + } + + this.output({ + success: true, + conversationId: args.id, + promotedInboxIds: promoted, + count: promoted.length, + admins: group.listAdmins(), + superAdmins: group.listSuperAdmins(), + }); + } +} From 8a53e71d5b3ec585fbb3a01d6057440d8b023337 Mon Sep 17 00:00:00 2001 From: Saul Carlin Date: Thu, 30 Apr 2026 10:52:15 -0700 Subject: [PATCH 2/4] feat(conversation): add 'remove-admin' command Co-Authored-By: Claude Sonnet 4.6 --- src/commands/conversation/remove-admin.ts | 49 +++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/commands/conversation/remove-admin.ts diff --git a/src/commands/conversation/remove-admin.ts b/src/commands/conversation/remove-admin.ts new file mode 100644 index 0000000..a967194 --- /dev/null +++ b/src/commands/conversation/remove-admin.ts @@ -0,0 +1,49 @@ +import { Args } from "@oclif/core"; +import { requireGroup } from "../../utils/xmtp.js"; +import { ConvosBaseCommand } from "../../baseCommand.js"; +import { createClientForIdentity } from "../../utils/client.js"; +import { createIdentityStore } from "../../utils/identities.js"; + +export default class ConversationRemoveAdmin extends ConvosBaseCommand { + static description = `Demote admins to regular members by inbox ID. Requires super admin permissions.`; + + static strict = false; + + static args = { + id: Args.string({ description: "The conversation ID", required: true }), + }; + + static flags = { ...ConvosBaseCommand.baseFlags }; + + async run(): Promise { + const { args, argv } = await this.parse(ConversationRemoveAdmin); + const inboxIds = (argv as string[]).slice(1); + if (inboxIds.length === 0) this.error("At least one inbox ID is required"); + + const config = this.getConvosConfig(); + const store = createIdentityStore(this.getConvosHome()); + const identity = store.getByConversationId(args.id); + if (!identity) this.error(`No identity found for conversation: ${args.id}`); + + const client = await createClientForIdentity(identity, config, this.getConvosHome()); + const conversation = await client.conversations.getConversationById(args.id); + if (!conversation) this.error(`Conversation not found: ${args.id}`); + + const group = requireGroup(conversation); + + const demoted: string[] = []; + for (const inboxId of inboxIds) { + await group.removeAdmin(inboxId); + demoted.push(inboxId); + } + + this.output({ + success: true, + conversationId: args.id, + demotedInboxIds: demoted, + count: demoted.length, + admins: group.listAdmins(), + superAdmins: group.listSuperAdmins(), + }); + } +} From 0f23c571a9ef89b567e290bed35359d4115d6603 Mon Sep 17 00:00:00 2001 From: Saul Carlin Date: Thu, 30 Apr 2026 10:58:36 -0700 Subject: [PATCH 3/4] feat(conversation): add 'add-super-admin' command --- src/commands/conversation/add-super-admin.ts | 53 ++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/commands/conversation/add-super-admin.ts diff --git a/src/commands/conversation/add-super-admin.ts b/src/commands/conversation/add-super-admin.ts new file mode 100644 index 0000000..0b5a13b --- /dev/null +++ b/src/commands/conversation/add-super-admin.ts @@ -0,0 +1,53 @@ +import { Args } from "@oclif/core"; +import { requireGroup } from "../../utils/xmtp.js"; +import { ConvosBaseCommand } from "../../baseCommand.js"; +import { createClientForIdentity } from "../../utils/client.js"; +import { createIdentityStore } from "../../utils/identities.js"; + +export default class ConversationAddSuperAdmin extends ConvosBaseCommand { + static description = `Promote members to super admin by inbox ID. Requires super admin permissions. + +Super admins can add and remove other admins, remove members, lock the +conversation, and explode it. Use this to transfer ownership of a +conversation to another member.`; + + static strict = false; + + static args = { + id: Args.string({ description: "The conversation ID", required: true }), + }; + + static flags = { ...ConvosBaseCommand.baseFlags }; + + async run(): Promise { + const { args, argv } = await this.parse(ConversationAddSuperAdmin); + const inboxIds = (argv as string[]).slice(1); + if (inboxIds.length === 0) this.error("At least one inbox ID is required"); + + const config = this.getConvosConfig(); + const store = createIdentityStore(this.getConvosHome()); + const identity = store.getByConversationId(args.id); + if (!identity) this.error(`No identity found for conversation: ${args.id}`); + + const client = await createClientForIdentity(identity, config, this.getConvosHome()); + const conversation = await client.conversations.getConversationById(args.id); + if (!conversation) this.error(`Conversation not found: ${args.id}`); + + const group = requireGroup(conversation); + + const promoted: string[] = []; + for (const inboxId of inboxIds) { + await group.addSuperAdmin(inboxId); + promoted.push(inboxId); + } + + this.output({ + success: true, + conversationId: args.id, + promotedInboxIds: promoted, + count: promoted.length, + admins: group.listAdmins(), + superAdmins: group.listSuperAdmins(), + }); + } +} From 7f64db362c2faa24ae5ff372b0ae9aa6569f3d74 Mon Sep 17 00:00:00 2001 From: Saul Carlin Date: Thu, 30 Apr 2026 10:59:44 -0700 Subject: [PATCH 4/4] feat(conversation): add 'remove-super-admin' command --- .../conversation/remove-super-admin.ts | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/commands/conversation/remove-super-admin.ts diff --git a/src/commands/conversation/remove-super-admin.ts b/src/commands/conversation/remove-super-admin.ts new file mode 100644 index 0000000..6be042a --- /dev/null +++ b/src/commands/conversation/remove-super-admin.ts @@ -0,0 +1,52 @@ +import { Args } from "@oclif/core"; +import { requireGroup } from "../../utils/xmtp.js"; +import { ConvosBaseCommand } from "../../baseCommand.js"; +import { createClientForIdentity } from "../../utils/client.js"; +import { createIdentityStore } from "../../utils/identities.js"; + +export default class ConversationRemoveSuperAdmin extends ConvosBaseCommand { + static description = `Demote super admins by inbox ID. Requires super admin permissions. + +A super admin can demote themselves with this command, provided at +least one other super admin remains in the conversation.`; + + static strict = false; + + static args = { + id: Args.string({ description: "The conversation ID", required: true }), + }; + + static flags = { ...ConvosBaseCommand.baseFlags }; + + async run(): Promise { + const { args, argv } = await this.parse(ConversationRemoveSuperAdmin); + const inboxIds = (argv as string[]).slice(1); + if (inboxIds.length === 0) this.error("At least one inbox ID is required"); + + const config = this.getConvosConfig(); + const store = createIdentityStore(this.getConvosHome()); + const identity = store.getByConversationId(args.id); + if (!identity) this.error(`No identity found for conversation: ${args.id}`); + + const client = await createClientForIdentity(identity, config, this.getConvosHome()); + const conversation = await client.conversations.getConversationById(args.id); + if (!conversation) this.error(`Conversation not found: ${args.id}`); + + const group = requireGroup(conversation); + + const demoted: string[] = []; + for (const inboxId of inboxIds) { + await group.removeSuperAdmin(inboxId); + demoted.push(inboxId); + } + + this.output({ + success: true, + conversationId: args.id, + demotedInboxIds: demoted, + count: demoted.length, + admins: group.listAdmins(), + superAdmins: group.listSuperAdmins(), + }); + } +}