feat: add distributor research functionality with detailed candidate information and outreach options

This commit is contained in:
Victor Noguera
2026-05-25 15:30:41 -04:00
parent 9b45546476
commit 313677692b
4 changed files with 243 additions and 117 deletions

View File

@@ -2,6 +2,7 @@ import index from "./web/index.html";
import * as XLSX from "xlsx";
import { normalizeAsin } from "./asin.ts";
import { db, client } from "./db/index.ts";
import { eq } from "drizzle-orm";
import {
analysisRevisions,
productDistributorResearch,
@@ -639,8 +640,8 @@ function normalizeDistributorCandidates(payload: unknown): DistributorCandidate[
rationale: String(item.rationale ?? "").trim(),
confidence: clampDistributorConfidence(item.confidence),
reputation: String(item.reputation ?? "").trim(),
contactInfo: String(item.contact_info ?? "").trim(),
outreachDraft: String(item.outreach_draft ?? "").trim(),
contactInfo: String(item.contact_info ?? item.contactInfo ?? "").trim(),
outreachDraft: String(item.outreach_draft ?? item.outreachDraft ?? "").trim(),
}))
.filter((item) => item.name.length > 0 && item.website.length > 0)
.slice(0, 10);
@@ -674,8 +675,8 @@ async function requestClaudeDistributorCandidates(context: Record<string, unknow
"For each distributor:",
"1. Identify whether they are an official brand distributor, authorized reseller, or national wholesaler.",
"2. Investigate their reputation: check for BBB accreditation, industry tenure, any known complaints or red flags, and whether they appear on the brand's own authorized-distributor list.",
"3. Find the most direct point-of-contact for new wholesale accounts — ideally a named buyer, vendor relations team, or wholesale inquiry email/phone from their website (not a generic contact form).",
"4. Draft a short, professional cold-outreach message (35 sentences) I can send to open a wholesale account inquiry. The message should: mention the specific product or brand, state that I sell on Amazon as an FBA seller, ask about minimum order quantities and wholesale pricing, and invite them to share an application or terms sheet.",
"3. Find the most direct point-of-contact for opening a new wholesale account. Search the distributor's website for a dedicated wholesale, reseller, or new-account page. Return AS MANY of these as you can find: full name and title of the wholesale/vendor relations contact, direct email address (e.g. wholesale@..., newaccounts@..., sales@...), direct phone number, and the URL of the wholesale application or inquiry page. If a named contact is not publicly listed, return the best department email and phone. Do NOT return a generic contact form URL as the only answer.",
"4. Draft a short, professional cold-outreach message (35 sentences) I can copy-paste and send. Tone: warm, genuine, and business-oriented — the goal is to start a relationship, not close a deal. Rules: (a) Praise the brand's reputation, quality, or market position sincerely — make it specific to what this brand is known for. (b) Frame the inquiry as a mutual growth opportunity; express eagerness to carry their line and help it reach more customers. (c) Do NOT mention Amazon, FBA, or online marketplaces anywhere in the message — present yourself simply as a retailer / reseller interested in carrying their products. (d) Ask about wholesale account requirements and invite them to share terms or an application. (e) Keep it concise and human — avoid corporate filler phrases.",
"",
"Return a raw JSON array. Each object must have exactly these keys:",
' "name" — distributor company name',
@@ -683,8 +684,8 @@ async function requestClaudeDistributorCandidates(context: Record<string, unknow
' "rationale" — why this distributor is a strong candidate (12 sentences)',
' "confidence" — integer 0100 reflecting how confident you are this is a real authorized source',
' "reputation" — summary of reputation findings (BBB status, years in business, any red flags)',
' "contact_info" — best point-of-contact found (name/title, email or phone, or wholesale inquiry URL)',
' "outreach_draft"— ready-to-send outreach message addressed to the contact above',
' "contact_info" — structured string with all contact details found: "Name: ..., Title: ..., Email: ..., Phone: ..., Wholesale page: ..."',
' "outreach_draft"— complete ready-to-send message addressed to the specific contact',
"",
"Product context:",
JSON.stringify(context, null, 2),
@@ -800,6 +801,9 @@ async function findDistributorsForStalkerProduct(runItemId: number) {
})),
};
const claude = await requestClaudeDistributorCandidates(promptContext);
await db
.delete(productDistributorResearch)
.where(eq(productDistributorResearch.runItemId, runItemId));
const [saved] = await db
.insert(productDistributorResearch)
.values({