salesprompter-cli 0.1.22 → 0.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/cli.js +379 -0
- package/dist/deel-outreach.js +454 -0
- package/dist/deel-salesnav.js +368 -0
- package/dist/direct-path.js +6 -5
- package/dist/domain.js +3 -0
- package/dist/instantly.js +2 -1
- package/dist/leadlists-funnel.js +30 -13
- package/package.json +10 -17
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
const COMPANY_SUFFIX_PATTERNS = [
|
|
2
|
+
/\s+GmbH\s*&\s*Co\.?\s+KG\.?$/i,
|
|
3
|
+
/\s+GmbH\.?$/i,
|
|
4
|
+
/\s+AG\.?$/i,
|
|
5
|
+
/\s+SE\.?$/i,
|
|
6
|
+
/\s+KG\.?$/i,
|
|
7
|
+
/\s+OHG\.?$/i,
|
|
8
|
+
/\s+GbR\.?$/i,
|
|
9
|
+
/\s+S\.A\.?$/i,
|
|
10
|
+
/\s+SA\.?$/i,
|
|
11
|
+
/\s+SARL\.?$/i,
|
|
12
|
+
/\s+LLC\.?$/i,
|
|
13
|
+
/\s+Inc\.?$/i,
|
|
14
|
+
/\s+Corp\.?$/i,
|
|
15
|
+
/\s+Ltd\.?$/i,
|
|
16
|
+
/\s+Group$/i
|
|
17
|
+
];
|
|
18
|
+
const DACH_SIGNAL_MATCHERS = [
|
|
19
|
+
{ label: "Germany", pattern: /(^|[^a-z])(germany|deutschland|bundesrepublik)([^a-z]|$)/i },
|
|
20
|
+
{ label: "Austria", pattern: /(^|[^a-z])(austria|österreich|oesterreich)([^a-z]|$)/i },
|
|
21
|
+
{ label: "Switzerland", pattern: /(^|[^a-z])(switzerland|schweiz|suisse|svizzera)([^a-z]|$)/i },
|
|
22
|
+
{ label: "Berlin", pattern: /(^|[^a-z])berlin([^a-z]|$)/i },
|
|
23
|
+
{ label: "Hamburg", pattern: /(^|[^a-z])hamburg([^a-z]|$)/i },
|
|
24
|
+
{ label: "Munich", pattern: /(^|[^a-z])(munich|münchen|muenchen)([^a-z]|$)/i },
|
|
25
|
+
{ label: "Frankfurt", pattern: /(^|[^a-z])frankfurt([^a-z]|$)/i },
|
|
26
|
+
{ label: "Cologne", pattern: /(^|[^a-z])(cologne|köln|koeln)([^a-z]|$)/i },
|
|
27
|
+
{ label: "Stuttgart", pattern: /(^|[^a-z])stuttgart([^a-z]|$)/i },
|
|
28
|
+
{ label: "Düsseldorf", pattern: /(^|[^a-z])(düsseldorf|duesseldorf)([^a-z]|$)/i },
|
|
29
|
+
{ label: "Vienna", pattern: /(^|[^a-z])(vienna|wien)([^a-z]|$)/i },
|
|
30
|
+
{ label: "Graz", pattern: /(^|[^a-z])graz([^a-z]|$)/i },
|
|
31
|
+
{ label: "Linz", pattern: /(^|[^a-z])linz([^a-z]|$)/i },
|
|
32
|
+
{ label: "Salzburg", pattern: /(^|[^a-z])salzburg([^a-z]|$)/i },
|
|
33
|
+
{ label: "Zurich", pattern: /(^|[^a-z])(zurich|zürich|zuerich)([^a-z]|$)/i },
|
|
34
|
+
{ label: "Geneva", pattern: /(^|[^a-z])(geneva|genf)([^a-z]|$)/i },
|
|
35
|
+
{ label: "Basel", pattern: /(^|[^a-z])basel([^a-z]|$)/i },
|
|
36
|
+
{ label: "Bern", pattern: /(^|[^a-z])bern([^a-z]|$)/i },
|
|
37
|
+
{ label: "Lausanne", pattern: /(^|[^a-z])lausanne([^a-z]|$)/i },
|
|
38
|
+
{ label: "Zug", pattern: /(^|[^a-z])zug([^a-z]|$)/i }
|
|
39
|
+
];
|
|
40
|
+
const DEEL_RELEVANT_TITLE_MATCHERS = [
|
|
41
|
+
/\bhead of (hr|human resources|people|people operations|people ops|people services|payroll|total rewards)\b/i,
|
|
42
|
+
/\b(head of compensation|head of benefits|head of people and culture)\b/i,
|
|
43
|
+
/\b(chief people officer|chro)\b/i,
|
|
44
|
+
/\b(vp|vice president)\b.*\b(hr|human resources|people|payroll|total rewards|compensation|benefits)\b/i,
|
|
45
|
+
/\bdirector\b.*\b(hr|human resources|people|payroll|total rewards|compensation|benefits|global mobility)\b/i,
|
|
46
|
+
/\b(hr|human resources|people|payroll)\b.*\bdirector\b/i,
|
|
47
|
+
/\b(hr business partner|hrbp)\b/i,
|
|
48
|
+
/\b(people operations manager|people ops manager|hr operations manager|payroll manager|benefits manager|compensation manager|global mobility manager)\b/i,
|
|
49
|
+
/\b(global payroll|global mobility|compensation and benefits)\b/i,
|
|
50
|
+
/\b(leiter personal|leiter hr|leiter human resources|personalleitung|personalleitung|bereichsleiter personal)\b/i
|
|
51
|
+
];
|
|
52
|
+
const DEEL_TITLE_EXCLUSION_MATCHERS = [
|
|
53
|
+
/\bassistant\b/i,
|
|
54
|
+
/\bassistent/i,
|
|
55
|
+
/\brecruit/i,
|
|
56
|
+
/\btalent acquisition\b/i,
|
|
57
|
+
/\bhead chef\b/i,
|
|
58
|
+
/\bceo\b/i,
|
|
59
|
+
/\bchief executive officer\b/i,
|
|
60
|
+
/\bfounder\b/i,
|
|
61
|
+
/\bco-founder\b/i,
|
|
62
|
+
/\bpresident\b/i,
|
|
63
|
+
/\bhead of sales\b/i,
|
|
64
|
+
/\bmarketing\b/i
|
|
65
|
+
];
|
|
66
|
+
function normalizeNullableString(value) {
|
|
67
|
+
const trimmed = value?.trim() ?? "";
|
|
68
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
69
|
+
}
|
|
70
|
+
export function cleanDeelCompanyName(value) {
|
|
71
|
+
let next = normalizeNullableString(value);
|
|
72
|
+
if (!next) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
for (const pattern of COMPANY_SUFFIX_PATTERNS) {
|
|
76
|
+
next = next.replace(pattern, "").trim();
|
|
77
|
+
}
|
|
78
|
+
return next.length > 0 ? next : normalizeNullableString(value);
|
|
79
|
+
}
|
|
80
|
+
export function isDeelRelevantSalesNavTitle(value) {
|
|
81
|
+
const normalized = normalizeNullableString(value);
|
|
82
|
+
if (!normalized) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
if (DEEL_TITLE_EXCLUSION_MATCHERS.some((pattern) => pattern.test(normalized))) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
return DEEL_RELEVANT_TITLE_MATCHERS.some((pattern) => pattern.test(normalized));
|
|
89
|
+
}
|
|
90
|
+
export function extractLinkedInCompanyHandleFromUrl(value) {
|
|
91
|
+
const normalized = normalizeNullableString(value);
|
|
92
|
+
if (!normalized) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const url = new URL(normalized);
|
|
97
|
+
if (!/(^|\.)linkedin\.com$/i.test(url.hostname)) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
const segments = url.pathname.split("/").filter((segment) => segment.length > 0);
|
|
101
|
+
const companyIndex = segments.findIndex((segment) => segment.toLowerCase() === "company");
|
|
102
|
+
const handle = companyIndex >= 0 ? segments[companyIndex + 1] ?? "" : "";
|
|
103
|
+
return handle.trim().length > 0 ? handle.trim().toLowerCase() : null;
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function preferredProfileUrl(row) {
|
|
110
|
+
return (normalizeNullableString(row.linkedin_profile_url) ??
|
|
111
|
+
normalizeNullableString(row.default_profile_url) ??
|
|
112
|
+
row.sales_nav_profile_url);
|
|
113
|
+
}
|
|
114
|
+
function collectDachSignals(value) {
|
|
115
|
+
if (!value) {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
const matches = [];
|
|
119
|
+
for (const matcher of DACH_SIGNAL_MATCHERS) {
|
|
120
|
+
if (matcher.pattern.test(value)) {
|
|
121
|
+
matches.push(matcher.label);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return matches;
|
|
125
|
+
}
|
|
126
|
+
export function classifyDeelSalesNavLanguage(row) {
|
|
127
|
+
const signalSources = [
|
|
128
|
+
{ field: "location", value: normalizeNullableString(row.location) },
|
|
129
|
+
{ field: "companyLocation", value: normalizeNullableString(row.company_location) },
|
|
130
|
+
{ field: "searchQuery", value: normalizeNullableString(row.search_query) }
|
|
131
|
+
];
|
|
132
|
+
const languageSignals = [];
|
|
133
|
+
const signalFields = [];
|
|
134
|
+
for (const source of signalSources) {
|
|
135
|
+
const sourceSignals = collectDachSignals(source.value);
|
|
136
|
+
if (sourceSignals.length > 0) {
|
|
137
|
+
languageSignals.push(...sourceSignals);
|
|
138
|
+
signalFields.push(source.field);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const uniqueSignals = [...new Set(languageSignals)];
|
|
142
|
+
const uniqueFields = [...new Set(signalFields)];
|
|
143
|
+
if (uniqueSignals.length > 0) {
|
|
144
|
+
return {
|
|
145
|
+
language: "de",
|
|
146
|
+
marketSegment: "dach",
|
|
147
|
+
languageReason: `clear_dach_signal:${uniqueSignals.join("|")}`,
|
|
148
|
+
languageSignals: uniqueSignals,
|
|
149
|
+
signalFields: uniqueFields
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
language: "en",
|
|
154
|
+
marketSegment: "non-dach",
|
|
155
|
+
languageReason: "fallback_non_dach",
|
|
156
|
+
languageSignals: [],
|
|
157
|
+
signalFields: []
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
export function normalizeDeelSalesNavRow(row) {
|
|
161
|
+
const classification = classifyDeelSalesNavLanguage(row);
|
|
162
|
+
return {
|
|
163
|
+
id: row.id,
|
|
164
|
+
orgId: row.org_id,
|
|
165
|
+
runId: normalizeNullableString(row.run_id),
|
|
166
|
+
language: classification.language,
|
|
167
|
+
marketSegment: classification.marketSegment,
|
|
168
|
+
languageReason: classification.languageReason,
|
|
169
|
+
languageSignals: classification.languageSignals,
|
|
170
|
+
signalFields: classification.signalFields,
|
|
171
|
+
fullName: normalizeNullableString(row.full_name),
|
|
172
|
+
firstName: normalizeNullableString(row.first_name),
|
|
173
|
+
lastName: normalizeNullableString(row.last_name),
|
|
174
|
+
title: normalizeNullableString(row.title),
|
|
175
|
+
industry: normalizeNullableString(row.industry),
|
|
176
|
+
companyName: normalizeNullableString(row.company_name),
|
|
177
|
+
companyNameCleaned: cleanDeelCompanyName(row.company_name),
|
|
178
|
+
location: normalizeNullableString(row.location),
|
|
179
|
+
companyLocation: normalizeNullableString(row.company_location),
|
|
180
|
+
preferredProfileUrl: preferredProfileUrl(row),
|
|
181
|
+
linkedinProfileUrl: normalizeNullableString(row.linkedin_profile_url),
|
|
182
|
+
defaultProfileUrl: normalizeNullableString(row.default_profile_url),
|
|
183
|
+
salesNavProfileUrl: row.sales_nav_profile_url,
|
|
184
|
+
companyUrl: normalizeNullableString(row.company_url),
|
|
185
|
+
regularCompanyUrl: normalizeNullableString(row.regular_company_url),
|
|
186
|
+
companyLinkedInHandle: extractLinkedInCompanyHandleFromUrl(row.company_url) ??
|
|
187
|
+
extractLinkedInCompanyHandleFromUrl(row.regular_company_url),
|
|
188
|
+
searchQuery: normalizeNullableString(row.search_query),
|
|
189
|
+
scrapedAt: normalizeNullableString(row.scraped_at)
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function percentage(count, total) {
|
|
193
|
+
if (total <= 0) {
|
|
194
|
+
return 0;
|
|
195
|
+
}
|
|
196
|
+
return Number(((count / total) * 100).toFixed(2));
|
|
197
|
+
}
|
|
198
|
+
export function buildDeelSalesNavPack(orgId, rows) {
|
|
199
|
+
const locales = { de: [], en: [] };
|
|
200
|
+
const titleCounts = new Map();
|
|
201
|
+
const signalFieldCounts = {
|
|
202
|
+
location: 0,
|
|
203
|
+
companyLocation: 0,
|
|
204
|
+
searchQuery: 0,
|
|
205
|
+
none: 0
|
|
206
|
+
};
|
|
207
|
+
const fieldCounts = {
|
|
208
|
+
firstName: 0,
|
|
209
|
+
lastName: 0,
|
|
210
|
+
fullName: 0,
|
|
211
|
+
companyName: 0,
|
|
212
|
+
companyNameCleaned: 0,
|
|
213
|
+
preferredProfileUrl: 0,
|
|
214
|
+
linkedinProfileUrl: 0,
|
|
215
|
+
companyLinkedInHandle: 0,
|
|
216
|
+
location: 0,
|
|
217
|
+
companyLocation: 0,
|
|
218
|
+
searchQuery: 0
|
|
219
|
+
};
|
|
220
|
+
for (const row of rows) {
|
|
221
|
+
const prepared = normalizeDeelSalesNavRow(row);
|
|
222
|
+
locales[prepared.language].push(prepared);
|
|
223
|
+
if (prepared.title) {
|
|
224
|
+
titleCounts.set(prepared.title, (titleCounts.get(prepared.title) ?? 0) + 1);
|
|
225
|
+
}
|
|
226
|
+
if (prepared.signalFields.length === 0) {
|
|
227
|
+
signalFieldCounts.none += 1;
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
for (const field of prepared.signalFields) {
|
|
231
|
+
if (field === "location") {
|
|
232
|
+
signalFieldCounts.location += 1;
|
|
233
|
+
}
|
|
234
|
+
else if (field === "companyLocation") {
|
|
235
|
+
signalFieldCounts.companyLocation += 1;
|
|
236
|
+
}
|
|
237
|
+
else if (field === "searchQuery") {
|
|
238
|
+
signalFieldCounts.searchQuery += 1;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (prepared.firstName)
|
|
243
|
+
fieldCounts.firstName += 1;
|
|
244
|
+
if (prepared.lastName)
|
|
245
|
+
fieldCounts.lastName += 1;
|
|
246
|
+
if (prepared.fullName)
|
|
247
|
+
fieldCounts.fullName += 1;
|
|
248
|
+
if (prepared.companyName)
|
|
249
|
+
fieldCounts.companyName += 1;
|
|
250
|
+
if (prepared.companyNameCleaned)
|
|
251
|
+
fieldCounts.companyNameCleaned += 1;
|
|
252
|
+
if (prepared.preferredProfileUrl)
|
|
253
|
+
fieldCounts.preferredProfileUrl += 1;
|
|
254
|
+
if (prepared.linkedinProfileUrl)
|
|
255
|
+
fieldCounts.linkedinProfileUrl += 1;
|
|
256
|
+
if (prepared.companyLinkedInHandle)
|
|
257
|
+
fieldCounts.companyLinkedInHandle += 1;
|
|
258
|
+
if (prepared.location)
|
|
259
|
+
fieldCounts.location += 1;
|
|
260
|
+
if (prepared.companyLocation)
|
|
261
|
+
fieldCounts.companyLocation += 1;
|
|
262
|
+
if (prepared.searchQuery)
|
|
263
|
+
fieldCounts.searchQuery += 1;
|
|
264
|
+
}
|
|
265
|
+
const total = rows.length;
|
|
266
|
+
const localeCounts = {
|
|
267
|
+
de: locales.de.length,
|
|
268
|
+
en: locales.en.length
|
|
269
|
+
};
|
|
270
|
+
const fieldCoverage = {
|
|
271
|
+
firstName: { count: fieldCounts.firstName, percentage: percentage(fieldCounts.firstName, total) },
|
|
272
|
+
lastName: { count: fieldCounts.lastName, percentage: percentage(fieldCounts.lastName, total) },
|
|
273
|
+
fullName: { count: fieldCounts.fullName, percentage: percentage(fieldCounts.fullName, total) },
|
|
274
|
+
companyName: { count: fieldCounts.companyName, percentage: percentage(fieldCounts.companyName, total) },
|
|
275
|
+
companyNameCleaned: { count: fieldCounts.companyNameCleaned, percentage: percentage(fieldCounts.companyNameCleaned, total) },
|
|
276
|
+
preferredProfileUrl: { count: fieldCounts.preferredProfileUrl, percentage: percentage(fieldCounts.preferredProfileUrl, total) },
|
|
277
|
+
linkedinProfileUrl: { count: fieldCounts.linkedinProfileUrl, percentage: percentage(fieldCounts.linkedinProfileUrl, total) },
|
|
278
|
+
companyLinkedInHandle: { count: fieldCounts.companyLinkedInHandle, percentage: percentage(fieldCounts.companyLinkedInHandle, total) },
|
|
279
|
+
location: { count: fieldCounts.location, percentage: percentage(fieldCounts.location, total) },
|
|
280
|
+
companyLocation: { count: fieldCounts.companyLocation, percentage: percentage(fieldCounts.companyLocation, total) },
|
|
281
|
+
searchQuery: { count: fieldCounts.searchQuery, percentage: percentage(fieldCounts.searchQuery, total) }
|
|
282
|
+
};
|
|
283
|
+
return {
|
|
284
|
+
vendor: "deel",
|
|
285
|
+
source: "salesnav-supabase",
|
|
286
|
+
orgId,
|
|
287
|
+
total,
|
|
288
|
+
locales,
|
|
289
|
+
summary: {
|
|
290
|
+
localeCounts,
|
|
291
|
+
localePercentages: {
|
|
292
|
+
de: percentage(localeCounts.de, total),
|
|
293
|
+
en: percentage(localeCounts.en, total)
|
|
294
|
+
},
|
|
295
|
+
fieldCoverage,
|
|
296
|
+
signalFieldCounts,
|
|
297
|
+
topTitles: [...titleCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 20)
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
function csvEscape(value) {
|
|
302
|
+
const normalized = value ?? "";
|
|
303
|
+
return `"${normalized.replaceAll('"', '""')}"`;
|
|
304
|
+
}
|
|
305
|
+
export function buildDeelSalesNavCsvHeader() {
|
|
306
|
+
return [
|
|
307
|
+
"id",
|
|
308
|
+
"orgId",
|
|
309
|
+
"runId",
|
|
310
|
+
"language",
|
|
311
|
+
"marketSegment",
|
|
312
|
+
"languageReason",
|
|
313
|
+
"languageSignals",
|
|
314
|
+
"signalFields",
|
|
315
|
+
"fullName",
|
|
316
|
+
"firstName",
|
|
317
|
+
"lastName",
|
|
318
|
+
"title",
|
|
319
|
+
"industry",
|
|
320
|
+
"companyName",
|
|
321
|
+
"companyNameCleaned",
|
|
322
|
+
"location",
|
|
323
|
+
"companyLocation",
|
|
324
|
+
"preferredProfileUrl",
|
|
325
|
+
"linkedinProfileUrl",
|
|
326
|
+
"defaultProfileUrl",
|
|
327
|
+
"salesNavProfileUrl",
|
|
328
|
+
"companyUrl",
|
|
329
|
+
"regularCompanyUrl",
|
|
330
|
+
"companyLinkedInHandle",
|
|
331
|
+
"searchQuery",
|
|
332
|
+
"scrapedAt"
|
|
333
|
+
].join(",");
|
|
334
|
+
}
|
|
335
|
+
export function buildDeelSalesNavCsvLines(rows) {
|
|
336
|
+
return rows
|
|
337
|
+
.map((row) => [
|
|
338
|
+
row.id,
|
|
339
|
+
row.orgId,
|
|
340
|
+
row.runId ?? "",
|
|
341
|
+
row.language,
|
|
342
|
+
row.marketSegment,
|
|
343
|
+
row.languageReason,
|
|
344
|
+
row.languageSignals.join("|"),
|
|
345
|
+
row.signalFields.join("|"),
|
|
346
|
+
row.fullName ?? "",
|
|
347
|
+
row.firstName ?? "",
|
|
348
|
+
row.lastName ?? "",
|
|
349
|
+
row.title ?? "",
|
|
350
|
+
row.industry ?? "",
|
|
351
|
+
row.companyName ?? "",
|
|
352
|
+
row.companyNameCleaned ?? "",
|
|
353
|
+
row.location ?? "",
|
|
354
|
+
row.companyLocation ?? "",
|
|
355
|
+
row.preferredProfileUrl,
|
|
356
|
+
row.linkedinProfileUrl ?? "",
|
|
357
|
+
row.defaultProfileUrl ?? "",
|
|
358
|
+
row.salesNavProfileUrl,
|
|
359
|
+
row.companyUrl ?? "",
|
|
360
|
+
row.regularCompanyUrl ?? "",
|
|
361
|
+
row.companyLinkedInHandle ?? "",
|
|
362
|
+
row.searchQuery ?? "",
|
|
363
|
+
row.scrapedAt ?? ""
|
|
364
|
+
]
|
|
365
|
+
.map(csvEscape)
|
|
366
|
+
.join(","))
|
|
367
|
+
.join("\n");
|
|
368
|
+
}
|
package/dist/direct-path.js
CHANGED
|
@@ -6,7 +6,7 @@ function marketCountries(market) {
|
|
|
6
6
|
if (market === "europe") {
|
|
7
7
|
return ["DE", "AT", "CH", "NL", "GB", "FR", "SE", "DK"];
|
|
8
8
|
}
|
|
9
|
-
return [
|
|
9
|
+
return [];
|
|
10
10
|
}
|
|
11
11
|
function sqlStringList(values) {
|
|
12
12
|
return values.map((value) => `'${value.replaceAll("'", "\\'")}'`).join(", ");
|
|
@@ -54,7 +54,8 @@ export function buildDirectPathLeadExportSql(vendor, market, limit) {
|
|
|
54
54
|
if (vendor !== "deel") {
|
|
55
55
|
throw new Error(`Unsupported direct-path vendor: ${vendor}`);
|
|
56
56
|
}
|
|
57
|
-
const countries =
|
|
57
|
+
const countries = marketCountries(market);
|
|
58
|
+
const countryClause = countries.length > 0 ? `co.countryCode IN (${sqlStringList(countries)})` : "TRUE";
|
|
58
59
|
const broadTitlePredicate = broadTitlePredicateSql();
|
|
59
60
|
const titleExclusions = titleExclusionPredicateSql();
|
|
60
61
|
return `WITH hr_leadlists AS (
|
|
@@ -129,7 +130,7 @@ SELECT
|
|
|
129
130
|
FROM contacts c
|
|
130
131
|
JOIN hr_leadlists l ON c.leadListId = l.leadListId
|
|
131
132
|
LEFT JOIN companies co ON c.companyId = co.companyId
|
|
132
|
-
WHERE
|
|
133
|
+
WHERE ${countryClause}
|
|
133
134
|
AND co.domain IS NOT NULL
|
|
134
135
|
AND co.companySize IN ('200-499', '501-1000', '1001-5000', '5001-10.000', '10.000+')
|
|
135
136
|
AND (
|
|
@@ -140,7 +141,7 @@ QUALIFY ROW_NUMBER() OVER (PARTITION BY c.contactId ORDER BY co.company_ts DESC)
|
|
|
140
141
|
ORDER BY co.company_ts DESC
|
|
141
142
|
LIMIT ${limit}`;
|
|
142
143
|
}
|
|
143
|
-
function
|
|
144
|
+
export function classifyDirectPathSegment(title) {
|
|
144
145
|
const normalized = title.toLowerCase();
|
|
145
146
|
if (normalized.includes("payroll") ||
|
|
146
147
|
normalized.includes("people services") ||
|
|
@@ -275,7 +276,7 @@ export function segmentDirectPathRows(vendor, market, rows) {
|
|
|
275
276
|
};
|
|
276
277
|
for (const row of rows) {
|
|
277
278
|
const title = row.jobTitle ?? "";
|
|
278
|
-
const { segment, reason } =
|
|
279
|
+
const { segment, reason } = classifyDirectPathSegment(title);
|
|
279
280
|
segments[segment].push({
|
|
280
281
|
...row,
|
|
281
282
|
segment,
|
package/dist/domain.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
export const LeadCustomVariableValueSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]);
|
|
3
|
+
export const LeadCustomVariablesSchema = z.record(z.string(), LeadCustomVariableValueSchema);
|
|
2
4
|
export const IcpSchema = z.object({
|
|
3
5
|
name: z.string().min(1),
|
|
4
6
|
description: z.string().default(""),
|
|
@@ -33,6 +35,7 @@ export const LeadSchema = z.object({
|
|
|
33
35
|
email: z.string().email(),
|
|
34
36
|
source: z.string().min(1),
|
|
35
37
|
signals: z.array(z.string().min(1)).default([]),
|
|
38
|
+
customVariables: LeadCustomVariablesSchema.optional(),
|
|
36
39
|
});
|
|
37
40
|
export const EnrichedLeadSchema = LeadSchema.extend({
|
|
38
41
|
techStack: z.array(z.string().min(1)).default([]),
|
package/dist/instantly.js
CHANGED
|
@@ -43,7 +43,8 @@ function buildLeadPayload(lead, campaignId) {
|
|
|
43
43
|
salesprompter_outreach_fit: lead.outreachFit,
|
|
44
44
|
salesprompter_signals: lead.signals.join(", "),
|
|
45
45
|
salesprompter_tech_stack: lead.techStack.join(", "),
|
|
46
|
-
salesprompter_rationale: lead.rationale.join(" | ")
|
|
46
|
+
salesprompter_rationale: lead.rationale.join(" | "),
|
|
47
|
+
...(lead.customVariables ?? {})
|
|
47
48
|
}
|
|
48
49
|
};
|
|
49
50
|
}
|
package/dist/leadlists-funnel.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
function
|
|
1
|
+
function marketCountries(market) {
|
|
2
2
|
if (market === "dach") {
|
|
3
|
-
return
|
|
3
|
+
return ["DE", "AT", "CH"];
|
|
4
4
|
}
|
|
5
5
|
if (market === "europe") {
|
|
6
|
-
return
|
|
6
|
+
return ["DE", "AT", "CH", "NL", "GB", "FR", "SE", "DK"];
|
|
7
|
+
}
|
|
8
|
+
return [];
|
|
9
|
+
}
|
|
10
|
+
function marketCountrySql(market) {
|
|
11
|
+
const countries = marketCountries(market);
|
|
12
|
+
if (countries.length === 0) {
|
|
13
|
+
return null;
|
|
7
14
|
}
|
|
8
|
-
return
|
|
15
|
+
return countries.map((country) => `"${country}"`).join(", ");
|
|
9
16
|
}
|
|
10
17
|
function marketQueryTerms(market) {
|
|
11
18
|
if (market === "dach") {
|
|
@@ -30,6 +37,16 @@ export function buildLeadlistsFunnelQueries(vendor, market) {
|
|
|
30
37
|
const countries = marketCountrySql(market);
|
|
31
38
|
const queryTerms = marketQueryTerms(market);
|
|
32
39
|
const marketLikeClause = encodedLikeClause(queryTerms);
|
|
40
|
+
const countryInClause = countries === null ? "TRUE" : `c.countryCode IN (${countries})`;
|
|
41
|
+
const countryWithDomainClause = countries === null ? "c.domain IS NOT NULL" : `c.countryCode IN (${countries}) AND c.domain IS NOT NULL`;
|
|
42
|
+
const companyMarketClause = countries === null ? "TRUE" : `e.countryCode IN (${countries})`;
|
|
43
|
+
const companyUsableDomainClause = countries === null
|
|
44
|
+
? "e.domain IS NOT NULL AND COALESCE(e.domainBlacklisted, FALSE) = FALSE"
|
|
45
|
+
: `e.countryCode IN (${countries}) AND e.domain IS NOT NULL AND COALESCE(e.domainBlacklisted, FALSE) = FALSE`;
|
|
46
|
+
const companyDomainNotFoundClause = countries === null
|
|
47
|
+
? "COALESCE(e.company_emailDomainNotFound, FALSE) = TRUE"
|
|
48
|
+
: `e.countryCode IN (${countries}) AND COALESCE(e.company_emailDomainNotFound, FALSE) = TRUE`;
|
|
49
|
+
const downstreamCountryClause = countries === null ? "TRUE" : `UPPER(CAST(company_countryCode AS STRING)) IN (${countries})`;
|
|
33
50
|
return {
|
|
34
51
|
queryTerms,
|
|
35
52
|
stages: [
|
|
@@ -73,10 +90,10 @@ SELECT
|
|
|
73
90
|
COUNT(*) AS people_with_company_url,
|
|
74
91
|
COUNTIF(companyIdFromUrl IS NOT NULL) AS people_with_extractable_company_id,
|
|
75
92
|
COUNTIF(c.companyId IS NOT NULL) AS people_joined_to_company,
|
|
76
|
-
COUNTIF(
|
|
77
|
-
COUNTIF(
|
|
78
|
-
COUNTIF(
|
|
79
|
-
COUNTIF(
|
|
93
|
+
COUNTIF(${countryInClause}) AS people_in_market_company,
|
|
94
|
+
COUNTIF(${countryWithDomainClause}) AS people_in_market_company_with_domain,
|
|
95
|
+
COUNTIF(${countryInClause} AND p.firstName IS NOT NULL) AS people_in_market_with_first_name,
|
|
96
|
+
COUNTIF(${countryInClause} AND p.lastName IS NOT NULL) AS people_in_market_with_last_name
|
|
80
97
|
FROM people p
|
|
81
98
|
LEFT JOIN companies c ON p.companyIdFromUrl = c.companyId`
|
|
82
99
|
},
|
|
@@ -97,10 +114,10 @@ SELECT
|
|
|
97
114
|
COUNT(DISTINCT CONCAT(CAST(h.companyId AS STRING),"|",CAST(h.leadListId AS STRING))) AS distinct_company_search_rows,
|
|
98
115
|
COUNTIF(h.companyUrl IS NOT NULL OR h.regularCompanyUrl IS NOT NULL) AS with_linkedin_company_profile,
|
|
99
116
|
COUNTIF(e.handle IS NOT NULL) AS with_handle,
|
|
100
|
-
COUNTIF(
|
|
101
|
-
COUNTIF(e.countryCode IN (${countries}) AND e.domain IS NOT NULL) AS with_domain,
|
|
102
|
-
COUNTIF(
|
|
103
|
-
COUNTIF(
|
|
117
|
+
COUNTIF(${companyMarketClause}) AS in_market,
|
|
118
|
+
COUNTIF(${countries === null ? "e.domain IS NOT NULL" : `e.countryCode IN (${countries}) AND e.domain IS NOT NULL`}) AS with_domain,
|
|
119
|
+
COUNTIF(${companyUsableDomainClause}) AS with_usable_domain,
|
|
120
|
+
COUNTIF(${companyDomainNotFoundClause}) AS domain_not_found
|
|
104
121
|
FROM hr_company_search h
|
|
105
122
|
LEFT JOIN enriched e
|
|
106
123
|
ON CAST(h.companyId AS STRING) = CAST(e.companyId AS STRING)
|
|
@@ -118,7 +135,7 @@ LEFT JOIN enriched e
|
|
|
118
135
|
COUNTIF(email IS NOT NULL AND COALESCE(email_invalid, FALSE) = FALSE) AS with_valid_email
|
|
119
136
|
FROM \`icpidentifier.SalesGPT.leadPool_new\`
|
|
120
137
|
WHERE CAST(functionId AS STRING) = "12"
|
|
121
|
-
AND
|
|
138
|
+
AND ${downstreamCountryClause}`
|
|
122
139
|
}
|
|
123
140
|
],
|
|
124
141
|
notes: [
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "salesprompter-cli",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "JSON-first
|
|
3
|
+
"version": "0.1.23",
|
|
4
|
+
"description": "JSON-first workflow automation CLI for account discovery, data processing, and system sync.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"salesprompter": "dist/cli.js"
|
|
@@ -29,23 +29,16 @@
|
|
|
29
29
|
"access": "public"
|
|
30
30
|
},
|
|
31
31
|
"keywords": [
|
|
32
|
-
"sales",
|
|
33
32
|
"salesprompter",
|
|
34
33
|
"cli",
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"outreach",
|
|
44
|
-
"instantly",
|
|
45
|
-
"hubspot",
|
|
46
|
-
"llm",
|
|
47
|
-
"ai-agent",
|
|
48
|
-
"codex"
|
|
34
|
+
"automation",
|
|
35
|
+
"workflow",
|
|
36
|
+
"orchestration",
|
|
37
|
+
"data",
|
|
38
|
+
"integration",
|
|
39
|
+
"sync",
|
|
40
|
+
"json",
|
|
41
|
+
"tooling"
|
|
49
42
|
],
|
|
50
43
|
"homepage": "https://salesprompter-cli.vercel.app",
|
|
51
44
|
"repository": {
|