Rename
This commit is contained in:
parent
1928077e02
commit
22eb3d52a1
@ -1,19 +1,19 @@
|
||||
# @clawdbot/agentmail
|
||||
# @moltbot/agentmail
|
||||
|
||||
Email channel plugin for Clawdbot via [AgentMail](https://agentmail.to).
|
||||
Email channel plugin for Moltbot via [AgentMail](https://agentmail.to).
|
||||
|
||||
## Installation
|
||||
|
||||
From npm:
|
||||
|
||||
```bash
|
||||
clawdbot plugins install @clawdbot/agentmail
|
||||
moltbot plugins install @moltbot/agentmail
|
||||
```
|
||||
|
||||
From local checkout:
|
||||
|
||||
```bash
|
||||
clawdbot plugins install ./extensions/agentmail
|
||||
moltbot plugins install ./extensions/agentmail
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { ClawdbotPluginApi } from "clawdbot/plugin-sdk";
|
||||
import type { MoltbotPluginApi } from "clawdbot/plugin-sdk";
|
||||
import { emptyPluginConfigSchema } from "clawdbot/plugin-sdk";
|
||||
|
||||
import { agentmailPlugin } from "./src/channel.js";
|
||||
@ -9,7 +9,7 @@ const plugin = {
|
||||
name: "AgentMail",
|
||||
description: "Email channel plugin via AgentMail API",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
register(api: ClawdbotPluginApi) {
|
||||
register(api: MoltbotPluginApi) {
|
||||
setAgentMailRuntime(api.runtime);
|
||||
api.registerChannel({ plugin: agentmailPlugin });
|
||||
},
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "@clawdbot/agentmail",
|
||||
"name": "@moltbot/agentmail",
|
||||
"version": "2026.1.26",
|
||||
"type": "module",
|
||||
"description": "Clawdbot AgentMail email channel plugin",
|
||||
"clawdbot": {
|
||||
"description": "Moltbot AgentMail email channel plugin",
|
||||
"moltbot": {
|
||||
"extensions": [
|
||||
"./index.ts"
|
||||
],
|
||||
@ -18,7 +18,7 @@
|
||||
"quickstartAllowFrom": true
|
||||
},
|
||||
"install": {
|
||||
"npmSpec": "@clawdbot/agentmail",
|
||||
"npmSpec": "@moltbot/agentmail",
|
||||
"localPath": "extensions/agentmail",
|
||||
"defaultChoice": "npm"
|
||||
}
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "clawdbot/plugin-sdk";
|
||||
|
||||
import type { AgentMailConfig, CoreConfig, ResolvedAgentMailAccount } from "./utils.js";
|
||||
import type {
|
||||
AgentMailConfig,
|
||||
CoreConfig,
|
||||
ResolvedAgentMailAccount,
|
||||
} from "./utils.js";
|
||||
|
||||
/**
|
||||
* Lists all AgentMail account IDs.
|
||||
@ -33,14 +37,15 @@ export type ResolvedAgentMailCredentials = {
|
||||
*/
|
||||
export function resolveCredentials(
|
||||
cfg: CoreConfig,
|
||||
env: Record<string, string | undefined> = process.env,
|
||||
env: Record<string, string | undefined> = process.env
|
||||
): ResolvedAgentMailCredentials {
|
||||
const base = cfg.channels?.agentmail ?? {};
|
||||
return {
|
||||
apiKey: base.token || env.AGENTMAIL_TOKEN,
|
||||
inboxId: base.emailAddress || env.AGENTMAIL_EMAIL_ADDRESS,
|
||||
webhookUrl: base.webhookUrl || env.AGENTMAIL_WEBHOOK_URL,
|
||||
webhookPath: base.webhookPath || env.AGENTMAIL_WEBHOOK_PATH || "/webhooks/agentmail",
|
||||
webhookPath:
|
||||
base.webhookPath || env.AGENTMAIL_WEBHOOK_PATH || "/webhooks/agentmail",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -140,7 +140,8 @@ export const agentmailPlugin: ChannelPlugin<ResolvedAgentMailAccount> = {
|
||||
validateInput: ({ input }) => {
|
||||
if (input.useEnv) return null;
|
||||
if (!input.token?.trim()) return "AgentMail requires --token";
|
||||
if (!input.emailAddress?.trim()) return "AgentMail requires --email-address";
|
||||
if (!input.emailAddress?.trim())
|
||||
return "AgentMail requires --email-address";
|
||||
return null;
|
||||
},
|
||||
applyAccountConfig: ({ cfg, input }) => {
|
||||
@ -152,11 +153,17 @@ export const agentmailPlugin: ChannelPlugin<ResolvedAgentMailAccount> = {
|
||||
agentmail: {
|
||||
...existing,
|
||||
enabled: true,
|
||||
...(input.useEnv ? {} : {
|
||||
...(input.token?.trim() ? { token: input.token.trim() } : {}),
|
||||
...(input.emailAddress?.trim() ? { emailAddress: input.emailAddress.trim() } : {}),
|
||||
...(input.webhookPath?.trim() ? { webhookPath: input.webhookPath.trim() } : {}),
|
||||
}),
|
||||
...(input.useEnv
|
||||
? {}
|
||||
: {
|
||||
...(input.token?.trim() ? { token: input.token.trim() } : {}),
|
||||
...(input.emailAddress?.trim()
|
||||
? { emailAddress: input.emailAddress.trim() }
|
||||
: {}),
|
||||
...(input.webhookPath?.trim()
|
||||
? { webhookPath: input.webhookPath.trim() }
|
||||
: {}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -16,18 +16,32 @@ export type MonitorAgentMailOptions = {
|
||||
};
|
||||
|
||||
// Runtime state tracking
|
||||
const runtimeState = new Map<string, {
|
||||
running: boolean;
|
||||
lastStartAt: number | null;
|
||||
lastStopAt: number | null;
|
||||
lastError: string | null;
|
||||
lastInboundAt?: number | null;
|
||||
lastOutboundAt?: number | null;
|
||||
}>();
|
||||
const runtimeState = new Map<
|
||||
string,
|
||||
{
|
||||
running: boolean;
|
||||
lastStartAt: number | null;
|
||||
lastStopAt: number | null;
|
||||
lastError: string | null;
|
||||
lastInboundAt?: number | null;
|
||||
lastOutboundAt?: number | null;
|
||||
}
|
||||
>();
|
||||
|
||||
function recordState(accountId: string, state: Partial<typeof runtimeState extends Map<string, infer V> ? V : never>) {
|
||||
function recordState(
|
||||
accountId: string,
|
||||
state: Partial<typeof runtimeState extends Map<string, infer V> ? V : never>
|
||||
) {
|
||||
const key = `agentmail:${accountId}`;
|
||||
runtimeState.set(key, { ...runtimeState.get(key) ?? { running: false, lastStartAt: null, lastStopAt: null, lastError: null }, ...state });
|
||||
runtimeState.set(key, {
|
||||
...(runtimeState.get(key) ?? {
|
||||
running: false,
|
||||
lastStartAt: null,
|
||||
lastStopAt: null,
|
||||
lastError: null,
|
||||
}),
|
||||
...state,
|
||||
});
|
||||
}
|
||||
|
||||
export function getAgentMailRuntimeState(accountId: string) {
|
||||
@ -47,7 +61,9 @@ function sendJson(res: ServerResponse, status: number, data: unknown) {
|
||||
}
|
||||
|
||||
/** Builds message body from webhook payload as fallback when API fetch fails. */
|
||||
function buildFallbackBody(message: NonNullable<MessageReceivedEvent["message"]>): string {
|
||||
function buildFallbackBody(
|
||||
message: NonNullable<MessageReceivedEvent["message"]>
|
||||
): string {
|
||||
const subject = message.subject ? `Subject: ${message.subject}\n\n` : "";
|
||||
return `${subject}${extractMessageBody(message)}`;
|
||||
}
|
||||
@ -98,7 +114,11 @@ export async function monitorAgentMailProvider(
|
||||
const allowlist = agentmailConfig?.allowlist ?? [];
|
||||
const blocklist = agentmailConfig?.blocklist ?? [];
|
||||
|
||||
recordState(accountId, { running: true, lastStartAt: Date.now(), lastError: null });
|
||||
recordState(accountId, {
|
||||
running: true,
|
||||
lastStartAt: Date.now(),
|
||||
lastError: null,
|
||||
});
|
||||
logger.info(`AgentMail: starting monitor for ${inboxId}`);
|
||||
|
||||
/**
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { AgentMailClient } from "agentmail";
|
||||
import type {
|
||||
ChannelOnboardingAdapter,
|
||||
ClawdbotConfig,
|
||||
MoltbotConfig,
|
||||
} from "clawdbot/plugin-sdk";
|
||||
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "clawdbot/plugin-sdk";
|
||||
|
||||
@ -51,9 +51,9 @@ async function listInboxes(client: AgentMailClient): Promise<string[]> {
|
||||
|
||||
/** Helper to build config with agentmail channel updates. */
|
||||
export function updateAgentMailConfig(
|
||||
cfg: ClawdbotConfig,
|
||||
cfg: MoltbotConfig,
|
||||
updates: Partial<AgentMailConfig>
|
||||
): ClawdbotConfig {
|
||||
): MoltbotConfig {
|
||||
const channels = (cfg.channels ?? {}) as Record<string, unknown>;
|
||||
const agentmail = (channels.agentmail ?? {}) as AgentMailConfig;
|
||||
return {
|
||||
@ -65,7 +65,7 @@ export function updateAgentMailConfig(
|
||||
...updates,
|
||||
},
|
||||
},
|
||||
} as ClawdbotConfig;
|
||||
} as MoltbotConfig;
|
||||
}
|
||||
|
||||
export const agentmailOnboardingAdapter: ChannelOnboardingAdapter = {
|
||||
@ -93,7 +93,7 @@ export const agentmailOnboardingAdapter: ChannelOnboardingAdapter = {
|
||||
? normalizeAccountId(accountOverrides.agentmail)
|
||||
: defaultAccountId;
|
||||
|
||||
let next = cfg as ClawdbotConfig;
|
||||
let next = cfg as MoltbotConfig;
|
||||
const account = resolveAgentMailAccount({
|
||||
cfg: next as CoreConfig,
|
||||
accountId,
|
||||
@ -164,7 +164,8 @@ export const agentmailOnboardingAdapter: ChannelOnboardingAdapter = {
|
||||
|
||||
// Ask if gateway has a public URL
|
||||
const hasPublicUrl = await prompter.confirm({
|
||||
message: "Does your gateway have a public URL? (for automatic webhook setup)",
|
||||
message:
|
||||
"Does your gateway have a public URL? (for automatic webhook setup)",
|
||||
initialValue: false,
|
||||
});
|
||||
|
||||
@ -180,13 +181,18 @@ export const agentmailOnboardingAdapter: ChannelOnboardingAdapter = {
|
||||
validate: (v) => {
|
||||
const trimmed = v?.trim();
|
||||
if (!trimmed) return "Required";
|
||||
if (!trimmed.startsWith("http://") && !trimmed.startsWith("https://")) {
|
||||
if (
|
||||
!trimmed.startsWith("http://") &&
|
||||
!trimmed.startsWith("https://")
|
||||
) {
|
||||
return "Must start with http:// or https://";
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
})
|
||||
).trim().replace(/\/+$/, ""); // Remove trailing slashes
|
||||
)
|
||||
.trim()
|
||||
.replace(/\/+$/, ""); // Remove trailing slashes
|
||||
|
||||
// Ask for webhook path
|
||||
const customPath = await prompter.confirm({
|
||||
@ -214,7 +220,7 @@ export const agentmailOnboardingAdapter: ChannelOnboardingAdapter = {
|
||||
await client.webhooks.create({
|
||||
url: `${baseUrl}${webhookPath}`,
|
||||
eventTypes: ["message.received"],
|
||||
clientId: `clawdbot-${emailAddress}`, // Idempotent per inbox
|
||||
clientId: `moltbot-${emailAddress}`, // Idempotent per inbox
|
||||
});
|
||||
await prompter.note(
|
||||
`Webhook registered: ${baseUrl}${webhookPath}`,
|
||||
@ -266,7 +272,8 @@ export const agentmailOnboardingAdapter: ChannelOnboardingAdapter = {
|
||||
if (addAllowlist) {
|
||||
const entry = String(
|
||||
await prompter.text({
|
||||
message: "Email or domain to allow (e.g., user@example.com or example.com)",
|
||||
message:
|
||||
"Email or domain to allow (e.g., user@example.com or example.com)",
|
||||
})
|
||||
).trim();
|
||||
|
||||
@ -306,7 +313,7 @@ export const agentmailOnboardingAdapter: ChannelOnboardingAdapter = {
|
||||
" Event: message.received",
|
||||
"",
|
||||
"The gateway must be publicly accessible for webhooks to work.",
|
||||
"Without this, Clawdbot won't receive incoming emails.",
|
||||
"Without this, Moltbot won't receive incoming emails.",
|
||||
].join("\n"),
|
||||
"Webhook Setup Required"
|
||||
);
|
||||
|
||||
@ -36,14 +36,20 @@ async function sendMessage(params: {
|
||||
/** Outbound adapter for the AgentMail channel. */
|
||||
export const agentmailOutbound: ChannelOutboundAdapter = {
|
||||
deliveryMode: "direct",
|
||||
chunker: (text, limit) => getAgentMailRuntime().channel.text.chunkMarkdownText(text, limit),
|
||||
chunker: (text, limit) =>
|
||||
getAgentMailRuntime().channel.text.chunkMarkdownText(text, limit),
|
||||
chunkerMode: "markdown",
|
||||
textChunkLimit: 4000,
|
||||
|
||||
sendText: ({ to, text, replyToId }) => sendMessage({ to, text, replyToId: replyToId ?? undefined }),
|
||||
sendText: ({ to, text, replyToId }) =>
|
||||
sendMessage({ to, text, replyToId: replyToId ?? undefined }),
|
||||
|
||||
sendMedia: ({ to, text, mediaUrl, replyToId }) => {
|
||||
const fullText = mediaUrl ? `${text}\n\nAttachment: ${mediaUrl}` : text;
|
||||
return sendMessage({ to, text: fullText, replyToId: replyToId ?? undefined });
|
||||
return sendMessage({
|
||||
to,
|
||||
text: fullText,
|
||||
replyToId: replyToId ?? undefined,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
@ -21,7 +21,6 @@ export type CoreConfig = {
|
||||
/** Resolved AgentMail account with runtime state. */
|
||||
export type ResolvedAgentMailAccount = {
|
||||
accountId: string;
|
||||
/** Account name for identifying this configuration. */
|
||||
name?: string;
|
||||
enabled: boolean;
|
||||
configured: boolean;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user