salesprompter-cli 0.1.52 → 0.1.54
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 +2 -1
- package/dist/cli.js +91 -29
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -72,7 +72,8 @@ salesprompter leads:collect \
|
|
|
72
72
|
|
|
73
73
|
# Enrich one or more stored imports: Sales Navigator company -> official domain -> Hunter email
|
|
74
74
|
# This updates workspace data only. It does not create or start outreach.
|
|
75
|
-
# Stale
|
|
75
|
+
# Stale ids and raw legal-name misses use conservative exact-name recovery.
|
|
76
|
+
# Hunter prefers official domains and can fall back to the exact company name.
|
|
76
77
|
salesprompter leads:enrich-import \
|
|
77
78
|
--run-id "$CLI_IMPORT_RUN_ID"
|
|
78
79
|
|
package/dist/cli.js
CHANGED
|
@@ -284,6 +284,8 @@ const CliImportHunterBatchResponseSchema = z.object({
|
|
|
284
284
|
found: z.number().int().nonnegative(),
|
|
285
285
|
notFound: z.number().int().nonnegative(),
|
|
286
286
|
failed: z.number().int().nonnegative(),
|
|
287
|
+
domainLookups: z.number().int().nonnegative().default(0),
|
|
288
|
+
companyLookups: z.number().int().nonnegative().default(0),
|
|
287
289
|
halted: z.boolean(),
|
|
288
290
|
error: z.string().nullable()
|
|
289
291
|
});
|
|
@@ -6756,7 +6758,11 @@ async function runCliImportEnrichmentAction(session, body, schema) {
|
|
|
6756
6758
|
return value;
|
|
6757
6759
|
}
|
|
6758
6760
|
async function getCliImportCompanyCandidatesViaApp(session, input) {
|
|
6759
|
-
return runCliImportEnrichmentAction(session, {
|
|
6761
|
+
return runCliImportEnrichmentAction(session, {
|
|
6762
|
+
action: "company-candidates",
|
|
6763
|
+
...input,
|
|
6764
|
+
retry: input.retryMode === "all",
|
|
6765
|
+
}, CliImportCompanyCandidatesResponseSchema);
|
|
6760
6766
|
}
|
|
6761
6767
|
async function saveCliImportCompanyProfilesViaApp(session, profiles) {
|
|
6762
6768
|
return runCliImportEnrichmentAction(session, { action: "save-companies", profiles }, CliImportCompanySaveResponseSchema);
|
|
@@ -13446,6 +13452,7 @@ function normalizeCompanyWebsite(value) {
|
|
|
13446
13452
|
}
|
|
13447
13453
|
}
|
|
13448
13454
|
const CLI_IMPORT_COMPANY_DECORATION = "(entityUrn,name,employeeCount,employeeDisplayCount,employeeCountRange,account(saved,noteCount,listCount,crmStatus,starred),pictureInfo,companyPictureDisplayImage,companyBackgroundCoverImage,description,industry,location,headquarters,website,revenueRange,crmOpportunities,flagshipCompanyUrl,employeeGrowthPercentages,employees*~fs_salesProfile(entityUrn,firstName,lastName,fullName,pictureInfo,profilePictureDisplayImage),specialties,type,yearFounded)";
|
|
13455
|
+
const CLI_IMPORT_COMPANY_RESOLUTION_VERSION = "cleaned-search-v1";
|
|
13449
13456
|
class CliImportCompanySessionError extends Error {
|
|
13450
13457
|
constructor(message) {
|
|
13451
13458
|
super(message);
|
|
@@ -13591,24 +13598,39 @@ async function resolveCliImportCompanyIdentity(candidate, config, timeoutMs, bro
|
|
|
13591
13598
|
let account = null;
|
|
13592
13599
|
if (!resolvedCompanyId && candidate.companyName) {
|
|
13593
13600
|
accountSearchUsed = true;
|
|
13594
|
-
const
|
|
13595
|
-
const
|
|
13596
|
-
|
|
13597
|
-
|
|
13598
|
-
|
|
13599
|
-
|
|
13600
|
-
|
|
13601
|
+
const seenSearchNames = new Set();
|
|
13602
|
+
const searchNames = [
|
|
13603
|
+
candidate.companyName,
|
|
13604
|
+
aggressivelyCleanLookupCompanyName(candidate.companyName),
|
|
13605
|
+
].filter((value) => {
|
|
13606
|
+
const key = normalizeLookupWhitespace(value).toLowerCase();
|
|
13607
|
+
if (!key || seenSearchNames.has(key))
|
|
13608
|
+
return false;
|
|
13609
|
+
seenSearchNames.add(key);
|
|
13610
|
+
return true;
|
|
13601
13611
|
});
|
|
13602
|
-
|
|
13603
|
-
|
|
13604
|
-
|
|
13605
|
-
|
|
13606
|
-
|
|
13607
|
-
|
|
13608
|
-
|
|
13609
|
-
:
|
|
13610
|
-
|
|
13611
|
-
|
|
13612
|
+
for (const searchName of searchNames) {
|
|
13613
|
+
const queryUrl = buildCliImportAccountSearchUrl(searchName);
|
|
13614
|
+
const response = await fetchCliImportSalesNavigatorJson({
|
|
13615
|
+
url: queryUrl,
|
|
13616
|
+
config,
|
|
13617
|
+
browserRelay,
|
|
13618
|
+
timeoutMs,
|
|
13619
|
+
label: `Sales Navigator Account Search for ${searchName}`,
|
|
13620
|
+
});
|
|
13621
|
+
if (response.status < 200 || response.status >= 300) {
|
|
13622
|
+
throw new Error(`Sales Navigator Account Search for ${searchName} returned ${response.status}.`);
|
|
13623
|
+
}
|
|
13624
|
+
if (response.body) {
|
|
13625
|
+
const accounts = extractLocalSalesNavigatorElements(response.body)
|
|
13626
|
+
.map((element) => typeof element === "object" && element !== null && !Array.isArray(element)
|
|
13627
|
+
? normalizeLocalSalesNavigatorAccount(element, queryUrl)
|
|
13628
|
+
: null)
|
|
13629
|
+
.filter((value) => value !== null);
|
|
13630
|
+
account = exactCliImportAccountMatch(candidate, accounts);
|
|
13631
|
+
}
|
|
13632
|
+
if (account)
|
|
13633
|
+
break;
|
|
13612
13634
|
}
|
|
13613
13635
|
salesNavCompanyUrl =
|
|
13614
13636
|
(typeof account?.salesNavCompanyUrl === "string"
|
|
@@ -13708,6 +13730,7 @@ function buildCliImportCompanyProfile(resolution, detail, input) {
|
|
|
13708
13730
|
error: input.error ??
|
|
13709
13731
|
`Sales Navigator company detail returned ${input.status || "no result"}`,
|
|
13710
13732
|
rawPayload: {
|
|
13733
|
+
resolutionVersion: CLI_IMPORT_COMPANY_RESOLUTION_VERSION,
|
|
13711
13734
|
accountSearchUsed: resolution.accountSearchUsed,
|
|
13712
13735
|
httpStatus: input.status,
|
|
13713
13736
|
},
|
|
@@ -13780,6 +13803,7 @@ function buildCliImportCompanyProfile(resolution, detail, input) {
|
|
|
13780
13803
|
status: "enriched",
|
|
13781
13804
|
error: null,
|
|
13782
13805
|
rawPayload: {
|
|
13806
|
+
resolutionVersion: CLI_IMPORT_COMPANY_RESOLUTION_VERSION,
|
|
13783
13807
|
accountSearchUsed: resolution.accountSearchUsed,
|
|
13784
13808
|
httpStatus: input.status,
|
|
13785
13809
|
detail,
|
|
@@ -13791,6 +13815,12 @@ async function runSalesNavigatorImportEnrichmentCommand(options) {
|
|
|
13791
13815
|
if (options.companiesOnly && options.hunterOnly) {
|
|
13792
13816
|
throw new Error("Use either --companies-only or --hunter-only, not both.");
|
|
13793
13817
|
}
|
|
13818
|
+
if (options.retryCompanies && options.retryNotFoundCompanies) {
|
|
13819
|
+
throw new Error("Use either --retry-companies or --retry-not-found-companies, not both.");
|
|
13820
|
+
}
|
|
13821
|
+
if (options.retryEmails && options.retryFailedEmails) {
|
|
13822
|
+
throw new Error("Use either --retry-emails or --retry-failed-emails, not both.");
|
|
13823
|
+
}
|
|
13794
13824
|
const runIds = Array.from(new Set(options.runId.map((runId) => z.string().uuid().parse(runId))));
|
|
13795
13825
|
const companyTimeoutMs = z.coerce.number().int().min(1000).max(120000).parse(options.companyTimeoutMs);
|
|
13796
13826
|
const companyDelayMinMs = z.coerce.number().int().min(0).parse(options.companyDelayMinMs);
|
|
@@ -13805,13 +13835,24 @@ async function runSalesNavigatorImportEnrichmentCommand(options) {
|
|
|
13805
13835
|
const session = await requireAuthSession();
|
|
13806
13836
|
const startedAt = new Date().toISOString();
|
|
13807
13837
|
const initialStatus = await getCliImportEnrichmentStatusViaApp(session, runIds);
|
|
13838
|
+
const companyRetryMode = options.retryCompanies
|
|
13839
|
+
? "all"
|
|
13840
|
+
: options.retryNotFoundCompanies
|
|
13841
|
+
? "not_found"
|
|
13842
|
+
: "default";
|
|
13843
|
+
const retryStatuses = options.retryEmails
|
|
13844
|
+
? ["not_found", "failed"]
|
|
13845
|
+
: options.retryFailedEmails
|
|
13846
|
+
? ["failed"]
|
|
13847
|
+
: [];
|
|
13808
13848
|
if (options.dryRun) {
|
|
13809
13849
|
const companyCandidates = options.hunterOnly
|
|
13810
13850
|
? []
|
|
13811
13851
|
: (await getCliImportCompanyCandidatesViaApp(session, {
|
|
13812
13852
|
runIds,
|
|
13813
13853
|
limit: 1000,
|
|
13814
|
-
|
|
13854
|
+
retryMode: companyRetryMode,
|
|
13855
|
+
resolutionVersion: CLI_IMPORT_COMPANY_RESOLUTION_VERSION,
|
|
13815
13856
|
})).companies.slice(0, maxCompanies || undefined);
|
|
13816
13857
|
return {
|
|
13817
13858
|
status: "ok",
|
|
@@ -13819,6 +13860,9 @@ async function runSalesNavigatorImportEnrichmentCommand(options) {
|
|
|
13819
13860
|
runIds,
|
|
13820
13861
|
initial: initialStatus,
|
|
13821
13862
|
companyCandidates: companyCandidates.length,
|
|
13863
|
+
companyRetryMode,
|
|
13864
|
+
hunterCompanyFallback: options.hunterCompanyFallback,
|
|
13865
|
+
retryEmailStatuses: retryStatuses,
|
|
13822
13866
|
next: options.companiesOnly
|
|
13823
13867
|
? "company enrichment only"
|
|
13824
13868
|
: options.hunterOnly
|
|
@@ -13838,7 +13882,8 @@ async function runSalesNavigatorImportEnrichmentCommand(options) {
|
|
|
13838
13882
|
const companyCandidates = (await getCliImportCompanyCandidatesViaApp(session, {
|
|
13839
13883
|
runIds,
|
|
13840
13884
|
limit: 1000,
|
|
13841
|
-
|
|
13885
|
+
retryMode: companyRetryMode,
|
|
13886
|
+
resolutionVersion: CLI_IMPORT_COMPANY_RESOLUTION_VERSION,
|
|
13842
13887
|
})).companies.slice(0, maxCompanies || undefined);
|
|
13843
13888
|
companies.selected = companyCandidates.length;
|
|
13844
13889
|
if (companyCandidates.length > 0) {
|
|
@@ -13887,12 +13932,18 @@ async function runSalesNavigatorImportEnrichmentCommand(options) {
|
|
|
13887
13932
|
for (const companyId of ids) {
|
|
13888
13933
|
const detail = cliImportCompanyRecord(results[companyId]);
|
|
13889
13934
|
const statusValue = statuses[companyId];
|
|
13935
|
+
const errorValue = errors[companyId];
|
|
13936
|
+
const errorRecord = cliImportCompanyRecord(errorValue);
|
|
13937
|
+
const errorStatusValue = errorRecord?.status ?? errorRecord?.code;
|
|
13890
13938
|
const status = typeof statusValue === "number"
|
|
13891
13939
|
? statusValue
|
|
13892
13940
|
: typeof statusValue === "string" && /^\d+$/.test(statusValue)
|
|
13893
13941
|
? Number(statusValue)
|
|
13894
|
-
:
|
|
13895
|
-
|
|
13942
|
+
: typeof errorStatusValue === "number"
|
|
13943
|
+
? errorStatusValue
|
|
13944
|
+
: typeof errorStatusValue === "string" && /^\d+$/.test(errorStatusValue)
|
|
13945
|
+
? Number(errorStatusValue)
|
|
13946
|
+
: response.status;
|
|
13896
13947
|
detailCache.set(companyId, {
|
|
13897
13948
|
detail,
|
|
13898
13949
|
status,
|
|
@@ -14082,15 +14133,21 @@ async function runSalesNavigatorImportEnrichmentCommand(options) {
|
|
|
14082
14133
|
processed: 0,
|
|
14083
14134
|
found: 0,
|
|
14084
14135
|
notFound: 0,
|
|
14085
|
-
failed: 0
|
|
14136
|
+
failed: 0,
|
|
14137
|
+
domainLookups: 0,
|
|
14138
|
+
companyLookups: 0,
|
|
14086
14139
|
};
|
|
14087
14140
|
if (!options.companiesOnly) {
|
|
14088
|
-
const retryBefore = options.retryEmails
|
|
14141
|
+
const retryBefore = options.retryEmails || options.retryFailedEmails
|
|
14142
|
+
? startedAt
|
|
14143
|
+
: null;
|
|
14089
14144
|
while (maxHunterBatches === 0 || hunter.batches < maxHunterBatches) {
|
|
14090
14145
|
const batch = await runCliImportHunterBatchViaApp(session, {
|
|
14091
14146
|
runIds,
|
|
14092
14147
|
limit: hunterBatchSize,
|
|
14093
|
-
retryBefore
|
|
14148
|
+
retryBefore,
|
|
14149
|
+
allowCompanyFallback: options.hunterCompanyFallback,
|
|
14150
|
+
retryStatuses,
|
|
14094
14151
|
});
|
|
14095
14152
|
if (batch.selected === 0)
|
|
14096
14153
|
break;
|
|
@@ -14100,7 +14157,9 @@ async function runSalesNavigatorImportEnrichmentCommand(options) {
|
|
|
14100
14157
|
processed: hunter.processed + batch.processed,
|
|
14101
14158
|
found: hunter.found + batch.found,
|
|
14102
14159
|
notFound: hunter.notFound + batch.notFound,
|
|
14103
|
-
failed: hunter.failed + batch.failed
|
|
14160
|
+
failed: hunter.failed + batch.failed,
|
|
14161
|
+
domainLookups: hunter.domainLookups + batch.domainLookups,
|
|
14162
|
+
companyLookups: hunter.companyLookups + batch.companyLookups,
|
|
14104
14163
|
};
|
|
14105
14164
|
writeProgress(`Hunter enrichment: ${hunter.processed} checked, ${hunter.found} emails found.`);
|
|
14106
14165
|
if (batch.halted) {
|
|
@@ -14239,9 +14298,12 @@ program
|
|
|
14239
14298
|
.option("--hunter-batch-size <number>", "Hunter contacts checked per request", "100")
|
|
14240
14299
|
.option("--max-hunter-batches <number>", "Maximum Hunter batches; 0 continues until no eligible people remain", "0")
|
|
14241
14300
|
.option("--companies-only", "Stop after company profiles and domains", false)
|
|
14242
|
-
.option("--hunter-only", "Skip company lookup and use
|
|
14243
|
-
.option("--retry-companies", "
|
|
14244
|
-
.option("--retry-
|
|
14301
|
+
.option("--hunter-only", "Skip company lookup and use stored domains or company-name fallback", false)
|
|
14302
|
+
.option("--retry-companies", "Refresh every previously saved company profile", false)
|
|
14303
|
+
.option("--retry-not-found-companies", "Retry only previous exact company misses with the current resolver", false)
|
|
14304
|
+
.option("--retry-emails", "Retry previous Hunter misses and failures once", false)
|
|
14305
|
+
.option("--retry-failed-emails", "Retry only previous Hunter failures once", false)
|
|
14306
|
+
.option("--no-hunter-company-fallback", "Require a verified domain and skip Hunter company-name fallback")
|
|
14245
14307
|
.option("--dry-run", "Show scope without LinkedIn or Hunter requests", false)
|
|
14246
14308
|
.option("--out <path>", "Optional JSON result path")
|
|
14247
14309
|
.action(async (options) => {
|
package/package.json
CHANGED