Files
mesh-catalog/modules/mailu/tools/index.ts
T

155 lines
6.2 KiB
TypeScript

// mailu's tools — moved here from the shared sdk (novox/hq ADR 0039), importing mailu's own client.
// They return structured data; the mesh serves them through the sdk's tool harness. Deletions are
// guarded by an explicit `confirm`, since removing a mailbox destroys its mail and cannot be undone.
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
import { MailuClient } from "../client.js";
export function getMailuTools(mailu: MailuClient): ToolDefinition[] {
return [
{
name: "mailu_list_users",
description: "List all email accounts on the mail server.",
input: {},
run: async () => {
const users = await mailu.listUsers();
return { count: users.length, users };
},
},
{
name: "mailu_create_user",
description: "Create a new email account.",
input: {
email: { type: "string", description: "full address, e.g. user@example.com" },
password: { type: "string", description: "the account's initial password" },
},
run: async (args) => {
const email = String(args.email);
await mailu.createUser(email, String(args.password));
return { created: email };
},
},
{
name: "mailu_change_password",
description: "Change the password of an email account.",
input: {
email: { type: "string", description: "full address of the account" },
password: { type: "string", description: "the new password" },
},
run: async (args) => {
const email = String(args.email);
await mailu.changePassword(email, String(args.password));
return { changed: email };
},
},
{
name: "mailu_delete_user",
description: "Delete an email account. DESTRUCTIVE — removes the mailbox and all its mail.",
input: {
email: { type: "string", description: "full address of the account to delete" },
confirm: { type: "boolean", description: "must be true — deletion is irreversible" },
},
run: async (args) => {
const email = String(args.email);
if (args.confirm !== true) return { aborted: "confirm must be true to delete a user", email };
await mailu.deleteUser(email);
return { deleted: email };
},
},
{
name: "mailu_list_aliases",
description: "List all email aliases and where they forward.",
input: {},
run: async () => {
const aliases = await mailu.listAliases();
return { count: aliases.length, aliases };
},
},
{
name: "mailu_create_alias",
description: "Create an email alias forwarding to one or more destinations.",
input: {
localpart: { type: "string", description: "the part before @, e.g. 'sales'" },
domain: { type: "string", description: "the domain, e.g. example.com" },
destination: { type: "string", description: "destination address(es), comma-separated" },
wildcard: { type: "boolean", description: "match any localpart under the domain (optional)" },
},
run: async (args) => {
const email = `${String(args.localpart)}@${String(args.domain)}`;
const destination = String(args.destination).split(",").map((d) => d.trim()).filter(Boolean);
await mailu.createAlias(email, destination, args.wildcard === true);
return { created: email, destination };
},
},
{
name: "mailu_delete_alias",
description: "Delete an email alias.",
input: {
email: { type: "string", description: "the alias address to delete" },
confirm: { type: "boolean", description: "must be true to confirm deletion" },
},
run: async (args) => {
const email = String(args.email);
if (args.confirm !== true) return { aborted: "confirm must be true to delete an alias", email };
await mailu.deleteAlias(email);
return { deleted: email };
},
},
{
name: "mailu_list_domains",
description: "List the mail domains the server handles.",
input: {},
run: async () => {
const domains = await mailu.listDomains();
return { count: domains.length, domains };
},
},
{
name: "mailu_read_mail",
description: "Read recent messages in a user's mailbox — date, from, subject and a preview.",
input: {
user: { type: "string", description: "the mailbox owner's address" },
mailbox: { type: "string", description: "which mailbox (default INBOX)" },
limit: { type: "number", description: "how many recent messages (default 10)" },
},
run: async (args) => {
const user = String(args.user);
const mailbox = args.mailbox ? String(args.mailbox) : "INBOX";
const messages = await mailu.readMail(user, mailbox, args.limit ? Number(args.limit) : 10);
return { user, mailbox, count: messages.length, messages };
},
},
{
name: "mailu_search_mail",
description: "Search a user's mailbox by subject, sender, and/or date.",
input: {
user: { type: "string", description: "the mailbox owner's address" },
subject: { type: "string", description: "substring to match in the subject (optional)" },
from: { type: "string", description: "sender address or name to match (optional)" },
since: { type: "string", description: "only messages since a date, e.g. 01-Jan-2026 (optional)" },
limit: { type: "number", description: "maximum results (default 10)" },
},
run: async (args) => {
const user = String(args.user);
const criteria = {
subject: args.subject ? String(args.subject) : undefined,
from: args.from ? String(args.from) : undefined,
since: args.since ? String(args.since) : undefined,
};
const messages = await mailu.searchMail(user, criteria, args.limit ? Number(args.limit) : 10);
return { user, count: messages.length, messages };
},
},
];
}
// The tools exist only when the admin API is configured; without it, mailu contributes none rather
// than failing the whole runtime.
registerModuleTools("mailu", (env) => {
try {
return getMailuTools(MailuClient.fromEnv(env));
} catch {
return [];
}
});