import { CloseBotClient, err } from "../client.js";
import type { ToolDefinition, ToolResult, LeadDto, LeadDtoPaginated } from "../types.js";
export const tools: ToolDefinition[] = [
{
name: "lead_manager_app",
description: "Searchable lead table with fields, conversation snippets, and status indicators. Returns HTML visualization.",
inputSchema: {
type: "object",
properties: {
sourceId: { type: "string", description: "Filter by source ID" },
page: { type: "number", description: "Page number (0-indexed)" },
pageSize: { type: "number", description: "Page size (default 20, max 100)" },
leadId: { type: "string", description: "Show details for a specific lead" },
},
},
},
];
function escapeHtml(s: string): string {
return s.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """);
}
function renderLeadTable(data: LeadDtoPaginated): string {
const leads = data.results || [];
const rows = leads
.map((lead) => {
const lastMsg = lead.lastMessage
? escapeHtml(lead.lastMessage.slice(0, 50))
: '—';
const direction =
lead.lastMessageDirection === "outbound"
? '→ out'
: lead.lastMessageDirection === "inbound"
? '← in'
: '—';
const time = lead.lastMessageTime
? new Date(lead.lastMessageTime).toLocaleString()
: "—";
const source = lead.source?.name || "—";
const fieldCount = lead.fields?.length || 0;
const instanceCount = lead.instances?.length || 0;
const failReason = lead.mostRecentFailureReason
? `
⚠️ ${escapeHtml(lead.mostRecentFailureReason.slice(0, 40))}
`
: "";
const tags =
lead.tags && lead.tags.length > 0
? lead.tags
.slice(0, 3)
.map((t) => `${escapeHtml(t)}`)
.join(" ")
: "";
return `
|
${escapeHtml(lead.name || "Unknown")}
${escapeHtml(lead.id || "")}
${tags ? `${tags} ` : ""}
|
${escapeHtml(source)} |
${direction}
${lastMsg}
${failReason}
|
${escapeHtml(time)} |
${fieldCount} |
${instanceCount} |
`;
})
.join("");
return `
👥 Lead Manager
${data.total ?? leads.length} total leads · Page ${(data.page ?? 0) + 1} · ${data.pageSize ?? 20} per page
| Lead |
Source |
Last Message |
Time |
Fields |
Bots |
${rows || '| No leads found |
'}
`;
}
function renderLeadDetail(lead: LeadDto): string {
const fields = (lead.fields || [])
.map(
(f) =>
`| ${escapeHtml(f.field || "")} | ${escapeHtml(f.value || "—")} |
`
)
.join("");
const instances = (lead.instances || [])
.map(
(i) =>
`| ${escapeHtml(i.botId || "")} | v${escapeHtml(i.botVersion || "?")} | ${i.followUpTime ? new Date(i.followUpTime).toLocaleString() : "—"} |
`
)
.join("");
const tags = (lead.tags || [])
.map((t) => `${escapeHtml(t)}`)
.join("");
return `
👤 ${escapeHtml(lead.name || "Unknown Lead")}
${escapeHtml(lead.id || "")}
Source
${escapeHtml(lead.source?.name || "—")}
Last Message
${escapeHtml((lead.lastMessage || "—").slice(0, 80))}
Contact ID
${escapeHtml(lead.contactId || "—")}
${tags ? `
${tags}
` : ""}
${lead.mostRecentFailureReason ? `
⚠️ ${escapeHtml(lead.mostRecentFailureReason)}
` : ""}
📋 Fields (${lead.fields?.length || 0})
🤖 Bot Instances (${lead.instances?.length || 0})
| Bot ID | Version | Follow-up |
${instances || '| No instances |
'}
`;
}
export async function handler(
client: CloseBotClient,
name: string,
args: Record
): Promise {
try {
if (args.leadId) {
const lead = await client.get(`/lead/${args.leadId}`);
const html = renderLeadDetail(lead);
return {
content: [{ type: "text", text: `Lead details: ${lead.name || lead.id}` }],
structuredContent: { type: "html", html },
};
}
const data = await client.get("/lead", {
page: args.page,
pageSize: args.pageSize || 20,
sourceId: args.sourceId,
});
const html = renderLeadTable(data);
return {
content: [{ type: "text", text: `${data.total ?? (data.results?.length || 0)} leads found` }],
structuredContent: { type: "html", html },
};
} catch (error) {
return err(error);
}
}