Improvements
This commit is contained in:
parent
cabba2ef91
commit
5f4a7fd917
@ -8,8 +8,8 @@ import {
|
||||
type ChannelPlugin,
|
||||
} from "clawdbot/plugin-sdk";
|
||||
|
||||
import { resolveAgentMailAccount, resolveCredentials } from "./accounts.js";
|
||||
import { getAgentMailClient } from "./client.js";
|
||||
import { resolveAgentMailAccount } from "./accounts.js";
|
||||
import { getAgentMailClient, getResolvedCredentials, NOT_CONFIGURED_ERROR } from "./client.js";
|
||||
import { AgentMailConfigSchema } from "./config-schema.js";
|
||||
import { agentmailOnboardingAdapter } from "./onboarding.js";
|
||||
import { agentmailOutbound } from "./outbound.js";
|
||||
@ -70,13 +70,9 @@ export const agentmailPlugin: ChannelPlugin<ResolvedAgentMailAccount> = {
|
||||
emailAddress: account.inboxId,
|
||||
}),
|
||||
resolveAllowFrom: ({ cfg }) =>
|
||||
((cfg as CoreConfig).channels?.agentmail?.allowFrom ?? []).map((entry) =>
|
||||
String(entry)
|
||||
),
|
||||
((cfg as CoreConfig).channels?.agentmail?.allowFrom ?? []).map(String),
|
||||
formatAllowFrom: ({ allowFrom }) =>
|
||||
allowFrom
|
||||
.map((entry) => String(entry).toLowerCase().trim())
|
||||
.filter(Boolean),
|
||||
allowFrom.map((e) => String(e).toLowerCase().trim()).filter(Boolean),
|
||||
},
|
||||
|
||||
security: {
|
||||
@ -98,12 +94,7 @@ export const agentmailPlugin: ChannelPlugin<ResolvedAgentMailAccount> = {
|
||||
messaging: {
|
||||
normalizeTarget: (raw) => raw.trim() || undefined,
|
||||
targetResolver: {
|
||||
looksLikeId: (raw) => {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return false;
|
||||
// Check if it looks like an email
|
||||
return trimmed.includes("@") && trimmed.includes(".");
|
||||
},
|
||||
looksLikeId: (raw) => /\S+@\S+\.\S+/.test(raw.trim()),
|
||||
hint: "<email@example.com>",
|
||||
},
|
||||
},
|
||||
@ -164,28 +155,15 @@ export const agentmailPlugin: ChannelPlugin<ResolvedAgentMailAccount> = {
|
||||
probe: s.probe,
|
||||
lastProbeAt: s.lastProbeAt ?? null,
|
||||
}),
|
||||
probeAccount: async ({ cfg }) => {
|
||||
probeAccount: async () => {
|
||||
try {
|
||||
const { apiKey, inboxId } = resolveCredentials(cfg as CoreConfig);
|
||||
if (!apiKey || !inboxId)
|
||||
return {
|
||||
ok: false,
|
||||
error: "Missing token or email address",
|
||||
elapsedMs: 0,
|
||||
};
|
||||
const { apiKey, inboxId } = getResolvedCredentials();
|
||||
if (!apiKey || !inboxId) return { ok: false, error: NOT_CONFIGURED_ERROR, elapsedMs: 0 };
|
||||
const start = Date.now();
|
||||
const inbox = await getAgentMailClient(apiKey).inboxes.get(inboxId);
|
||||
return {
|
||||
ok: true,
|
||||
elapsedMs: Date.now() - start,
|
||||
meta: { inboxId: inbox.inboxId },
|
||||
};
|
||||
return { ok: true, elapsedMs: Date.now() - start, meta: { inboxId: inbox.inboxId } };
|
||||
} catch (err) {
|
||||
return {
|
||||
ok: false,
|
||||
error: err instanceof Error ? err.message : String(err),
|
||||
elapsedMs: 0,
|
||||
};
|
||||
return { ok: false, error: err instanceof Error ? err.message : String(err), elapsedMs: 0 };
|
||||
}
|
||||
},
|
||||
buildAccountSnapshot: ({ account: a, runtime: r, probe }) => ({
|
||||
|
||||
@ -4,34 +4,31 @@ import { resolveCredentials } from "./accounts.js";
|
||||
import { getAgentMailRuntime } from "./runtime.js";
|
||||
import type { CoreConfig } from "./utils.js";
|
||||
|
||||
export const NOT_CONFIGURED_ERROR = "AgentMail not configured (missing token or email address)";
|
||||
|
||||
let sharedClient: AgentMailClient | null = null;
|
||||
let sharedClientKey: string | null = null;
|
||||
|
||||
/** Creates or returns a shared AgentMailClient instance. Recreates if key changed. */
|
||||
export function getAgentMailClient(apiKey?: string): AgentMailClient {
|
||||
const key = apiKey ?? getResolvedCredentials().apiKey;
|
||||
if (!key) throw new Error("AgentMail token is required");
|
||||
|
||||
// Return cached client if key matches
|
||||
if (sharedClient && sharedClientKey === key) {
|
||||
return sharedClient;
|
||||
}
|
||||
|
||||
// Create new client (key changed or first call)
|
||||
sharedClient = new AgentMailClient({ apiKey: key });
|
||||
sharedClientKey = key;
|
||||
return sharedClient;
|
||||
}
|
||||
|
||||
/** Resolves credentials from current config. */
|
||||
export function getResolvedCredentials() {
|
||||
return resolveCredentials(getAgentMailRuntime().config.loadConfig() as CoreConfig);
|
||||
}
|
||||
|
||||
/** Creates or returns a shared AgentMailClient instance. Recreates if key changed. */
|
||||
export function getAgentMailClient(apiKey?: string): AgentMailClient {
|
||||
const key = apiKey ?? getResolvedCredentials().apiKey;
|
||||
if (!key) throw new Error("AgentMail token is required");
|
||||
|
||||
if (sharedClient && sharedClientKey === key) return sharedClient;
|
||||
|
||||
sharedClient = new AgentMailClient({ apiKey: key });
|
||||
sharedClientKey = key;
|
||||
return sharedClient;
|
||||
}
|
||||
|
||||
/** Returns client and inboxId, or throws if not configured. */
|
||||
export function getClientAndInbox(): { client: AgentMailClient; inboxId: string } {
|
||||
const { apiKey, inboxId } = getResolvedCredentials();
|
||||
if (!apiKey || !inboxId) throw new Error("AgentMail not configured (missing token or email address)");
|
||||
if (!apiKey || !inboxId) throw new Error(NOT_CONFIGURED_ERROR);
|
||||
return { client: getAgentMailClient(apiKey), inboxId };
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import type { AgentMail } from "agentmail";
|
||||
|
||||
import { resolveCredentials } from "./accounts.js";
|
||||
import { getAgentMailClient } from "./client.js";
|
||||
import { getAgentMailClient, getResolvedCredentials, NOT_CONFIGURED_ERROR } from "./client.js";
|
||||
import { getAgentMailRuntime } from "./runtime.js";
|
||||
import { checkSenderAllowed, labelMessageAllowed } from "./filtering.js";
|
||||
import { extractMessageBody, fetchFormattedThread } from "./thread.js";
|
||||
@ -54,9 +53,9 @@ export async function monitorAgentMailProvider(
|
||||
};
|
||||
|
||||
const accountId = opts.accountId ?? "default";
|
||||
const { apiKey, inboxId } = resolveCredentials(cfg);
|
||||
const { apiKey, inboxId } = getResolvedCredentials();
|
||||
if (!apiKey || !inboxId) {
|
||||
logger.warn("AgentMail not configured (missing token or email address)");
|
||||
logger.warn(NOT_CONFIGURED_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -1,11 +1,8 @@
|
||||
import { AgentMailClient } from "agentmail";
|
||||
import type {
|
||||
ChannelOnboardingAdapter,
|
||||
MoltbotConfig,
|
||||
} from "clawdbot/plugin-sdk";
|
||||
import type { ChannelOnboardingAdapter, MoltbotConfig } from "clawdbot/plugin-sdk";
|
||||
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "clawdbot/plugin-sdk";
|
||||
|
||||
import { resolveAgentMailAccount, resolveCredentials } from "./accounts.js";
|
||||
import { resolveAgentMailAccount } from "./accounts.js";
|
||||
import type { AgentMailConfig, CoreConfig } from "./utils.js";
|
||||
|
||||
const channel = "agentmail" as const;
|
||||
@ -55,14 +52,13 @@ export const agentmailOnboardingAdapter: ChannelOnboardingAdapter = {
|
||||
channel,
|
||||
|
||||
getStatus: async ({ cfg }) => {
|
||||
const { apiKey, inboxId } = resolveCredentials(cfg as CoreConfig);
|
||||
const configured = Boolean(apiKey && inboxId);
|
||||
const account = resolveAgentMailAccount({ cfg: cfg as CoreConfig });
|
||||
return {
|
||||
channel,
|
||||
configured,
|
||||
statusLines: [`AgentMail: ${configured ? `configured (${inboxId})` : "needs token"}`],
|
||||
selectionHint: configured ? "configured" : "not configured",
|
||||
quickstartScore: configured ? 1 : 5,
|
||||
configured: account.configured,
|
||||
statusLines: [`AgentMail: ${account.configured ? `configured (${account.inboxId})` : "needs token"}`],
|
||||
selectionHint: account.configured ? "configured" : "not configured",
|
||||
quickstartScore: account.configured ? 1 : 5,
|
||||
};
|
||||
},
|
||||
|
||||
@ -73,9 +69,6 @@ export const agentmailOnboardingAdapter: ChannelOnboardingAdapter = {
|
||||
|
||||
let next = cfg as MoltbotConfig;
|
||||
const account = resolveAgentMailAccount({ cfg: next as CoreConfig, accountId });
|
||||
const { apiKey, inboxId } = resolveCredentials(next as CoreConfig);
|
||||
const configured = Boolean(apiKey && inboxId);
|
||||
|
||||
const canUseEnv = accountId === DEFAULT_ACCOUNT_ID && Boolean(process.env.AGENTMAIL_TOKEN?.trim());
|
||||
|
||||
// If env var token is available and not already configured, offer to use it
|
||||
@ -92,9 +85,9 @@ export const agentmailOnboardingAdapter: ChannelOnboardingAdapter = {
|
||||
}
|
||||
|
||||
// If already configured, ask to keep
|
||||
if (configured) {
|
||||
if (account.configured) {
|
||||
const keep = await prompter.confirm({
|
||||
message: `AgentMail already configured (${inboxId}). Keep current settings?`,
|
||||
message: `AgentMail already configured (${account.inboxId}). Keep current settings?`,
|
||||
initialValue: true,
|
||||
});
|
||||
if (keep) return { cfg: next };
|
||||
|
||||
@ -4,7 +4,7 @@ import type { ChannelOutboundAdapter } from "clawdbot/plugin-sdk";
|
||||
import { getClientAndInbox } from "./client.js";
|
||||
import { getAgentMailRuntime } from "./runtime.js";
|
||||
|
||||
/** Sends a reply to an email message via AgentMail. */
|
||||
/** Sends a reply-all to an email message via AgentMail. */
|
||||
export async function sendAgentMailReply(params: {
|
||||
client: AgentMailClient;
|
||||
inboxId: string;
|
||||
@ -12,24 +12,23 @@ export async function sendAgentMailReply(params: {
|
||||
text: string;
|
||||
html?: string;
|
||||
}): Promise<{ messageId: string; threadId: string }> {
|
||||
const { client, inboxId, messageId, text, html } = params;
|
||||
return client.inboxes.messages.reply(inboxId, messageId, { text, html });
|
||||
return params.client.inboxes.messages.replyAll(params.inboxId, params.messageId, {
|
||||
text: params.text,
|
||||
html: params.html,
|
||||
});
|
||||
}
|
||||
|
||||
/** Sends a message (reply or new) and returns standardized result. */
|
||||
/** Sends a message (reply-all or new) and returns standardized result. */
|
||||
async function sendMessage(params: {
|
||||
to: string;
|
||||
text: string;
|
||||
html?: string;
|
||||
replyToId?: string;
|
||||
}): Promise<{ channel: "agentmail"; messageId: string; threadId: string }> {
|
||||
const { to, text, html, replyToId } = params;
|
||||
const { client, inboxId } = getClientAndInbox();
|
||||
|
||||
const result = replyToId
|
||||
? await client.inboxes.messages.reply(inboxId, replyToId, { text, html })
|
||||
: await client.inboxes.messages.send(inboxId, { to: [to], text, html });
|
||||
|
||||
const result = params.replyToId
|
||||
? await client.inboxes.messages.replyAll(inboxId, params.replyToId, { text: params.text, html: params.html })
|
||||
: await client.inboxes.messages.send(inboxId, { to: [params.to], text: params.text, html: params.html });
|
||||
return { channel: "agentmail", ...result };
|
||||
}
|
||||
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
import type { AgentMailClient, AgentMail } from "agentmail";
|
||||
|
||||
import { formatAttachments } from "./attachment.js";
|
||||
import { formatUtcDate } from "./utils.js";
|
||||
import { formatUtcDate, type Message } from "./utils.js";
|
||||
|
||||
type Thread = AgentMail.threads.Thread;
|
||||
type Message = AgentMail.messages.Message;
|
||||
|
||||
/**
|
||||
* Extracts the body text from a message, preferring extractedText.
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { AgentMail } from "agentmail";
|
||||
import type { MoltbotConfig } from "clawdbot/plugin-sdk";
|
||||
import type { z } from "zod";
|
||||
|
||||
import type { AgentMailConfigSchema } from "./config-schema.js";
|
||||
@ -6,16 +7,9 @@ import type { AgentMailConfigSchema } from "./config-schema.js";
|
||||
/** AgentMail channel configuration. */
|
||||
export type AgentMailConfig = z.infer<typeof AgentMailConfigSchema>;
|
||||
|
||||
/** Core config shape with AgentMail channel. */
|
||||
export type CoreConfig = {
|
||||
channels?: {
|
||||
agentmail?: AgentMailConfig;
|
||||
};
|
||||
session?: {
|
||||
store?: string;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
[key: string]: unknown;
|
||||
/** Core config with AgentMail channel typed. */
|
||||
export type CoreConfig = MoltbotConfig & {
|
||||
channels?: { agentmail?: AgentMailConfig };
|
||||
};
|
||||
|
||||
/** Resolved AgentMail account with runtime state. */
|
||||
|
||||
Loading…
Reference in New Issue
Block a user