import { log } from "@clack/prompts" import { organizationRoles } from "@automate.ax/api-contract" import { defineCommand } from "../lib/command" import { globalOptions, globalOptionsSchema } from "../lib/global-options" import z from "zod" import { apiClient } from "../lib/api-client" import { ORG_OPT, orgOptsSchema, output, requireSession, safelyPromptTextWithValidation, sessionIntro, useOrganization, ORG_OPTION_DESCRIPTIONS, } from "../utils" import { accent, dimmed } from "../lib/io" import { clearSpinner, startSpinner, withSpinner } from "../lib/spinner" /** Organization roles assignable through an invitation. */ const roles = organizationRoles /** Arguments accepted by the organization invitation command. */ const inviteOrgOptsSchema = orgOptsSchema.extend({ positionals: z.tuple([z.email().optional()]), role: z.enum(roles).default("member"), }) export const orgInviteCommand = defineCommand({ name: "invite", description: "Invite a member to an organization", options: { ...globalOptions, role: { type: "string", short: "r", default: "member", }, ...ORG_OPT, }, optionDescriptions: { role: `Role to assign (${roles.join(", ")})`, ...ORG_OPTION_DESCRIPTIONS, }, positionals: [ { name: "email", description: "Email address to invite", required: false, }, ], schema: globalOptionsSchema.extend(inviteOrgOptsSchema.shape), run: inviteMember, }) /** * Invites a user to the resolved organization by email. * * @param opts - Validated organization, email, and role options. * @throws When the organization's seat limit has been reached. */ export async function inviteMember(opts: z.infer) { const session = await requireSession("invite organization members") output.normal(() => sessionIntro(session)) const org = await useOrganization(opts, { whenEmpty: "prompt-create" }) if ( !(await withSpinner("Checking seat limit", () => apiClient.org.canInvite({ organizationId: org.id }), )) ) { throw new Error( `Seat limit reached. Run \`automate org upgrade -o ${org.id}\` to add more members.`, ) } const email = opts.positionals[0] ?? (await safelyPromptTextWithValidation( { message: "Who should we invite?", placeholder: "john@example.com", }, z.email(), "email", )) startSpinner(`Inviting ${accent(email)}`) const invitation: Awaited> = await apiClient.org.invite({ email, role: opts.role, organizationId: org.id, }) clearSpinner() output .normal(() => log.success( `Invited ${accent(email)} as ${dimmed(opts.role)} to ${accent(org.name)}`, ), ) .json(invitation) }